ms-ole.c 60.1 KB
Newer Older
Michael Meeks's avatar
Michael Meeks committed
1
/**
2 3 4 5
 * ms-ole.c: MS Office OLE support for Gnumeric
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
Michael Meeks's avatar
Michael Meeks committed
6
 **/
7 8 9 10 11 12 13 14 15
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <malloc.h>
#include <assert.h>
#include <ctype.h>
16
#include <glib.h>
Michael Meeks's avatar
Michael Meeks committed
17 18 19 20 21 22 23 24 25

#define  MS_OLE_H_IMPLEMENTATION
#	include "ms-ole.h"
#undef   MS_OLE_H_IMPLEMENTATION

#ifndef MAP_FAILED
/* Someone needs their head examining - BSD ? */
#	define MAP_FAILED ((void *)-1)
#endif
26

27 28
/* Implementational detail - not for global header */

29
#define OLE_DEBUG 0
30

31 32 33 34 35
/* FIXME tenix add ADD_BBD_LIST_BLOCK where it should be used) */
#define ADD_BBD_LIST_BLOCK   0xfffffffc       /* -4 */
#define SPECIAL_BLOCK        0xfffffffd       /* -3 (BBD_LIST BLOCK) */
#define END_OF_CHAIN         0xfffffffe       /* -2 */
#define UNUSED_BLOCK         0xffffffff       /* -1 */
36

37
/* FIXME tenix laola reads this from the header */
Michael Meeks's avatar
Michael Meeks committed
38 39
#define BB_BLOCK_SIZE     512
#define SB_BLOCK_SIZE      64
40

41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59
/* FIXME tenix laola understand the next header:
      MAGIC     => undef,       #      00
      CLSID     => undef,       # guid 08
      REVISION  => undef,       # word 18
      VERSION   => undef,       # word 1a
      BYTEORDER => undef,       # word 1c
      B_S_LOG   => undef,       # word 1e       big block size = 2^b_s_log
      S_S_LOG   => undef,       # word 20       small block size = 2^s_s_log
      UK1       => undef,       # word(5) 22
      B_D_NUM   => undef,       # long 2c       bbd num of blocks
      ROOT_SB   => undef,       # long 30       root start block
      UK2       => undef,       # long 34
      B_S_MIN   => undef,       # long 38       minimum size of big_block
      S_D_SB    => undef,       # long 3c       sbd start block
      S_D_NUM   => undef,       # long 40       number of sbd blocks
      B_XD_SB   => undef,       # long 44
      B_XD_NUM  => undef,       # long 48
 */

Michael Meeks's avatar
Michael Meeks committed
60 61 62 63 64 65
/**
 * Structure describing an OLE file
 **/
struct _MsOle
{
	int        ref_count;
66
	gboolean   ole_mmap;
Michael Meeks's avatar
Michael Meeks committed
67 68 69 70 71 72 73 74 75 76 77
	guint8    *mem ;
	guint32    length ;
	
	char       mode;
	int        file_des;
	int        dirty;
	GArray    *bb;     /* Big  blocks status  */
	GArray    *sb;     /* Small block status  */
	GArray    *sbf;    /* The small block file */
	guint32    num_pps;/* Count of number of property sets */
	GList     *pps;    /* Property Storage -> struct _PPS, always 1 valid entry or NULL */
78 79 80
/* if memory mapped */
	GPtrArray *bbattr; /* Pointers to block structures */
/* end if memory mapped */
Michael Meeks's avatar
Michael Meeks committed
81 82
};

83 84 85 86 87 88
/* A global variable to enable calles to check_stream,
 * applications should optionally enable due to the performance penalty.
 * of 30-50 % of load time.
 */
gboolean libole2_debug = FALSE;

Michael Meeks's avatar
Michael Meeks committed
89 90
typedef guint32 PPS_IDX ;

91 92
#if OLE_DEBUG > 0
/* Very grim, but quite necessary */
93 94
#       define ms_array_index(a,b,c) (b)my_array_hack ((a), sizeof(b), (c))

95 96 97
static guint32
my_array_hack (GArray *a, guint s, guint32 idx)
{
98 99 100 101
	g_assert (a != NULL);
	g_assert (idx >= 0);
	g_assert (idx < a->len);
	g_assert (s == 4);
102 103 104 105
	return ((guint32 *)a->data)[idx];
}
#else
/* Far far faster... */
106
#       define ms_array_index(a,b,c) g_array_index (a, b, c)
107 108
#endif

109
#define BB_THRESHOLD   0x1000
Michael Meeks's avatar
Michael Meeks committed
110

111
#define PPS_ROOT_INDEX    0
112 113
#define PPS_BLOCK_SIZE 0x80
#define PPS_END_OF_CHAIN 0xffffffff
114

115 116
typedef struct _PPS PPS;

Michael Meeks's avatar
Michael Meeks committed
117 118 119
#define PPS_SIG 0x13579753
#define IS_PPS(p) (((PPS *)(p))->sig == PPS_SIG)

120
struct _PPS {
Michael Meeks's avatar
Michael Meeks committed
121
	int      sig;
122
	char    *name;
123 124
	GList   *children;
	PPS     *parent;
125 126
	guint32  size;
	BLP      start;
Michael Meeks's avatar
Michael Meeks committed
127
	MsOleType type;
128
	PPS_IDX  idx; /* Only used on write */
129 130
};

131
#define BB_R_PTR(f,b) ((f)->ole_mmap ? ((f)->mem + (b+1)*BB_BLOCK_SIZE) :     \
132
				       (get_block_ptr (f, b, FALSE)))
133
#define BB_W_PTR(f,b) ((f)->ole_mmap ?  BB_R_PTR(f,b) :			      \
134
				       (get_block_ptr (f, b, TRUE)))
135

136 137 138 139
#define GET_SB_R_PTR(f,b) (BB_R_PTR(f, g_array_index ((f)->sbf, BLP, (b)/(BB_BLOCK_SIZE/SB_BLOCK_SIZE))) \
			   + (((b)%(BB_BLOCK_SIZE/SB_BLOCK_SIZE))*SB_BLOCK_SIZE))
#define GET_SB_W_PTR(f,b) (BB_W_PTR(f, g_array_index ((f)->sbf, BLP, (b)/(BB_BLOCK_SIZE/SB_BLOCK_SIZE))) \
			   + (((b)%(BB_BLOCK_SIZE/SB_BLOCK_SIZE))*SB_BLOCK_SIZE))
140

141 142 143
#define MAX_CACHED_BLOCKS  32

typedef struct {
144
	guint32  blk;
145 146 147
	gboolean dirty;
	int      usage;
	guint8   *data;
Michael Meeks's avatar
Michael Meeks committed
148
} BBBlkAttr;
149

Michael Meeks's avatar
Michael Meeks committed
150
static BBBlkAttr *
151 152
bb_blk_attr_new (guint32 blk)
{
Michael Meeks's avatar
Michael Meeks committed
153
	BBBlkAttr *attr = g_new (BBBlkAttr, 1);
154 155 156 157 158 159 160 161
	attr->blk   = blk;
	attr->dirty = FALSE;
	attr->usage = 0;
	attr->data  = 0;
	return attr;
}

static void
Michael Meeks's avatar
Michael Meeks committed
162
write_cache_block (MsOle *f, BBBlkAttr *attr)
163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
{
	size_t offset;

	g_return_if_fail (f);
	g_return_if_fail (attr);
	g_return_if_fail (attr->data);
	
	offset = (attr->blk+1)*BB_BLOCK_SIZE;
	if (lseek (f->file_des, offset, SEEK_SET)==(off_t)-1 ||
	    write (f->file_des, attr->data, BB_BLOCK_SIZE) == -1)
		printf ("Fatal error writing block %d at %d\n", attr->blk, offset);
#if OLE_DEBUG > 0
	printf ("Writing cache block %d to offset %d\n",
		attr->blk, offset);
#endif	
	attr->dirty = FALSE;
}
180 181

static guint8 *
Michael Meeks's avatar
Michael Meeks committed
182
get_block_ptr (MsOle *f, BLP b, gboolean forwrite)
183
{
Michael Meeks's avatar
Michael Meeks committed
184
	BBBlkAttr *attr, *tmp, *min;
185
	size_t offset;
186
	guint32 i, blks;
187 188 189 190 191 192 193

	g_assert (f);
	g_assert (b < f->bbattr->len);

	/* Have we cached it ? */
	attr = g_ptr_array_index (f->bbattr, b);
	g_assert (attr);
194 195 196 197 198 199
	g_assert (attr->blk == b);

	if (attr->data) {
		attr->usage++;
		if (forwrite)
			attr->dirty = TRUE;
200
		return attr->data;
201
	}
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
	/* LRU strategy */
	min  = NULL;
	blks = 0;
	for (i=0;i<f->bbattr->len;i++) {
		tmp = g_ptr_array_index (f->bbattr, i);
		if (tmp->data) {
			blks++;
			if (!min)
				min = tmp;
		        else if (tmp->usage < min->usage)
				min = tmp;
		}
		tmp->usage = (guint32)tmp->usage*0.707;
	}
	if (blks < MAX_CACHED_BLOCKS)
		min = 0;

	g_assert (!attr->data);
	if (min) {
		g_assert (min->data);
#if EXCEL_DEBUG > 0
		printf ("Replacing cache block %d with %d\n", min->blk, b);
#endif
		if (min->dirty)
			write_cache_block (f, min);
		attr->data  = min->data;
		min->data   = 0;
		min->usage  = 0;
	} else
		attr->data = g_new (guint8, BB_BLOCK_SIZE);
	
234 235 236
	offset = (b+1)*BB_BLOCK_SIZE;
	lseek (f->file_des, offset, SEEK_SET);
	read (f->file_des, attr->data, BB_BLOCK_SIZE);
237 238
	attr->usage = 1;
	attr->dirty = forwrite;
239 240

	return attr->data;
241
}
242

243 244 245 246 247 248 249 250 251 252
/* This is a list of big blocks which contain a flat description of all blocks
   in the file. Effectively inside these blocks is a FAT of chains of other BBs,
   so the theoretical max size = 128 BB Fat blocks, thus = 128*512*512/4 blocks 
   ~= 8.4MBytes */
/* FIXME tenix the max size would actually be 109*512*512/4 + 512 blocks ~=
   7MBytes if we don't take in count the additional Big Block Depot lists.
   Number of additional lists is in header:0x48, the location of the first
   additional list is in header:0x44, the location of the second additional
   list is at the very end of the first additional list and so on, the last
   additional list have at the end a END_OF_CHAIN.
253
   Each additional list can address 128*512/4*512 blocks ~= 8MBytes */
254
/* The number of Big Block Descriptor (fat) Blocks */
255 256
#define GET_NUM_BBD_BLOCKS(f)   (MS_OLE_GET_GUINT32((f)->mem + 0x2c))
#define SET_NUM_BBD_BLOCKS(f,n) (MS_OLE_SET_GUINT32((f)->mem + 0x2c, (n)))
257
/* The block locations of the Big Block Descriptor Blocks */
258
#define MAX_SIZE_BBD_LIST           109
259
/* FIXME tenix next is broken with big files */
260
#define GET_BBD_LIST(f,i)           (MS_OLE_GET_GUINT32((f)->mem + 0x4c + (i)*4))
261
/* FIXME tenix next is broken with big files */
262
#define SET_BBD_LIST(f,i,n)         (MS_OLE_SET_GUINT32((f)->mem + 0x4c + (i)*4, (n)))
263 264
#define NEXT_BB(f,n)                (g_array_index ((f)->bb, BLP, n))
#define NEXT_SB(f,n)                (g_array_index ((f)->sb, BLP, n))
265 266 267 268 269
/* Additional Big Block Descriptor (fat) Blocks */
#define MAX_SIZE_ADD_BBD_LIST       127
#define GET_NUM_ADD_BBD_LISTS(f)   (MS_OLE_GET_GUINT32((f)->mem + 0x48))
#define GET_FIRST_ADD_BBD_LIST(f)  (MS_OLE_GET_GUINT32((f)->mem + 0x44))

Michael Meeks's avatar
Michael Meeks committed
270
/* Get the start block of the root directory ( PPS ) chain */
271 272
#define GET_ROOT_STARTBLOCK(f)   (MS_OLE_GET_GUINT32((f)->mem + 0x30))
#define SET_ROOT_STARTBLOCK(f,i) (MS_OLE_SET_GUINT32((f)->mem + 0x30, i))
Michael Meeks's avatar
Michael Meeks committed
273
/* Get the start block of the SBD chain */
274 275
#define GET_SBD_STARTBLOCK(f)    (MS_OLE_GET_GUINT32((f)->mem + 0x3c))
#define SET_SBD_STARTBLOCK(f,i)  (MS_OLE_SET_GUINT32((f)->mem + 0x3c, i))
276

277

Michael Meeks's avatar
Michael Meeks committed
278 279 280
/* NB it is misleading to assume that Microsofts linked lists link correctly.
   It is not the case that pps_next(f, pps_prev(f, n)) = n ! For the final list
   item there are no valid links. Cretins. */
281 282 283 284 285 286 287 288
#define PPS_GET_NAME_LEN(p)   (MS_OLE_GET_GUINT16(p + 0x40))
#define PPS_SET_NAME_LEN(p,i) (MS_OLE_SET_GUINT16(p + 0x40, (i)))
#define PPS_GET_PREV(p)   ((PPS_IDX) MS_OLE_GET_GUINT32(p + 0x44))
#define PPS_GET_NEXT(p)   ((PPS_IDX) MS_OLE_GET_GUINT32(p + 0x48))
#define PPS_GET_DIR(p)    ((PPS_IDX) MS_OLE_GET_GUINT32(p + 0x4c))
#define PPS_SET_PREV(p,i) ((PPS_IDX) MS_OLE_SET_GUINT32(p + 0x44, i))
#define PPS_SET_NEXT(p,i) ((PPS_IDX) MS_OLE_SET_GUINT32(p + 0x48, i))
#define PPS_SET_DIR(p,i)  ((PPS_IDX) MS_OLE_SET_GUINT32(p + 0x4c, i))
Michael Meeks's avatar
Michael Meeks committed
289
/* These get other interesting stuff from the PPS record */
290 291
#define PPS_GET_STARTBLOCK(p)      ( MS_OLE_GET_GUINT32(p + 0x74))
#define PPS_GET_SIZE(p)            ( MS_OLE_GET_GUINT32(p + 0x78))
Michael Meeks's avatar
Michael Meeks committed
292
#define PPS_GET_TYPE(p) ((MsOleType)( MS_OLE_GET_GUINT8(p + 0x42)))
293 294 295
#define PPS_SET_STARTBLOCK(p,i)    ( MS_OLE_SET_GUINT32(p + 0x74, i))
#define PPS_SET_SIZE(p,i)          ( MS_OLE_SET_GUINT32(p + 0x78, i))
#define PPS_SET_TYPE(p,i)          ( MS_OLE_SET_GUINT8 (p + 0x42, i))
296

297 298 299 300
/* Try to mark the Big Block "b" as as unused if it is marked as "c", in the
   FAT "f". */
#define TRY_MARK_UNUSED_BLOCK(f,block,mark) {                               \
        if (g_array_index ((f), BLP, (block)) != (mark)) {                  \
Arturo Tena/libole2's avatar
Arturo Tena/libole2 committed
301
        g_warning ("Tried to mark as unused the block %d which has %d\n",  \
302 303 304
                   (block), g_array_index ((f), BLP, (block)));             \
        } else { g_array_index ((f), BLP, (block)) = UNUSED_BLOCK; } }

305
/* FIXME: This needs proper unicode support ! current support is a guess */
Michael Meeks's avatar
Michael Meeks committed
306
/* Length is in bytes == 1/2 the final text length */
307 308
/* NB. Different from biff_get_text, looks like a bug ! */
static char *
309
pps_get_text (guint8 *ptr, int length)
310
{
311
	int lp;
Arturo Espinosa's avatar
Arturo Espinosa committed
312
	char *ans;
Michael Meeks's avatar
Michael Meeks committed
313
	guint16 c;
314
	guint8 *inb;
315
	
Michael Meeks's avatar
Michael Meeks committed
316 317
	length = (length+1)/2;

318
	if (length <= 0 ||
Michael Meeks's avatar
Michael Meeks committed
319 320 321 322
	    length > (PPS_BLOCK_SIZE/4)) {
#if OLE_DEBUG > 0
		printf ("Nulled name of length %d\n", length);
#endif
Arturo Espinosa's avatar
Arturo Espinosa committed
323
		return 0;
Michael Meeks's avatar
Michael Meeks committed
324
	}
325
	
326
	ans = (char *)g_malloc (sizeof(char) * length + 1);
327
	
328
	c = MS_OLE_GET_GUINT16(ptr);
Michael Meeks's avatar
Michael Meeks committed
329
	if (c<0x30) /* Magic unicode number I made up */
Arturo Espinosa's avatar
Arturo Espinosa committed
330
		inb = ptr + 2;
331
	else
Arturo Espinosa's avatar
Arturo Espinosa committed
332
		inb = ptr;
Michael Meeks's avatar
Michael Meeks committed
333
	for (lp=0;lp<length;lp++) {
334
		c = MS_OLE_GET_GUINT16(inb);
Michael Meeks's avatar
Michael Meeks committed
335
		ans[lp] = (char)c;
Arturo Espinosa's avatar
Arturo Espinosa committed
336
		inb+=2;
337
	}
Arturo Espinosa's avatar
Arturo Espinosa committed
338 339
	ans[lp] = 0;
	return ans;
340
}
341

342
static void
Michael Meeks's avatar
Michael Meeks committed
343
dump_header (MsOle *f)
344
{
Michael Meeks's avatar
Michael Meeks committed
345
	printf ("--------------------------MsOle HEADER-------------------------\n");
Michael Meeks's avatar
Michael Meeks committed
346
	printf ("Num BBD Blocks : %d Root %%d, SB blocks %d\n",
347
		f->bb?f->bb->len:-1,
348
/*		f->pps?f->pps->len:-1, */
349
/* FIXME tenix, here is not f->num_pps? */
350 351 352 353 354
		f->sb?f->sb->len:-1);
	printf ("-------------------------------------------------------------\n");
}

static void
Michael Meeks's avatar
Michael Meeks committed
355
characterise_block (MsOle *f, BLP blk, char **ans)
356
{
357
	int nblk;
358 359 360 361 362 363 364 365

	nblk = g_array_index (f->bb, BLP, blk);
	if (nblk == UNUSED_BLOCK) {
		*ans = "unused";
		return;
	} else if (nblk == SPECIAL_BLOCK) {
		*ans = "special";
		return;
366 367 368 369
	} else if (nblk == ADD_BBD_LIST_BLOCK) {
		*ans = "additional special";
		return;
	} else if (nblk == END_OF_CHAIN) {
370
		*ans = "end of chain";
371
		return;
372
	}
373

374 375 376 377 378
	*ans = "unknown";
	g_return_if_fail (f);
	g_return_if_fail (f->bb);
	g_return_if_fail (f->pps);

379
/*	for (lp=0;lp<f->pps->len;lp++) {
380 381 382 383 384 385 386 387 388 389 390 391
		PPS *p = g_ptr_array_index (f->pps, lp);
		BLP cur = p->start;
		while (cur != END_OF_CHAIN) {
			if (cur == SPECIAL_BLOCK ||
			    cur == UNUSED_BLOCK) {
				*ans = "serious block error";
				return;
			}
			if (cur == blk) {
				*ans = p->name;
				return;
			}
392
			cur = NEXT_BB (f, cur);
393
		}
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418
		}*/
}

static void
dump_tree (GList *list, int indent)
{
	PPS *p;
	int lp;
	char indentstr[64];
	g_return_if_fail (indent<60);

	for (lp=0;lp<indent;lp++)
		indentstr[lp]= '-';
	indentstr[lp]=0;

	while (list) {
		p = list->data;
		if (p) {
			printf ("%s '%s' - %d\n",
				indentstr, p->name, p->size);
			if (p->children)
				dump_tree (p->children, indent+1);
		} else
			printf ("%s NULL!\n", indentstr);
		list = g_list_next (list);
419 420 421 422
	}
}

static void
Michael Meeks's avatar
Michael Meeks committed
423
dump_allocation (MsOle *f)
424 425 426 427 428 429 430 431 432 433
{
	int lp;
	char *blktype;

	for (lp=0;lp<f->bb->len;lp++) {
		characterise_block (f, lp, &blktype);
		printf ("Block %d -> block %d ( '%s' )\n", lp,
			g_array_index (f->bb, BLP, lp),
			blktype);
	}
434
	
Michael Meeks's avatar
Michael Meeks committed
435
	if (f->pps) {
436 437
		printf ("Root blocks : %d\n", f->num_pps); 
		dump_tree (f->pps, 0);
Michael Meeks's avatar
Michael Meeks committed
438 439 440
	} else
		printf ("No root yet\n");
/*	
441 442
	printf ("sbd blocks : %d\n", h->sbd_list->len);
	for (lp=0;lp<h->sbd_list->len;lp++)
443
	printf ("sbd_list[%d] = %d\n", lp, (int)ms_array_index (h->sbd_list, SBPtr, lp));*/
Arturo Espinosa's avatar
Arturo Espinosa committed
444
	printf ("-------------------------------------------------------------\n");
445 446
}

447 448 449 450
/**
 * Dump some useful facts.
 **/
void
Michael Meeks's avatar
Michael Meeks committed
451
ms_ole_debug (MsOle *f, int magic)
452 453 454 455 456
{
	dump_header (f);
	dump_allocation (f);
}

457
static BLP
Michael Meeks's avatar
Michael Meeks committed
458
get_next_block (MsOle *f, BLP blk)
459
{
460 461
	/* blk is an index of the complete fat, and
	   an index of the complete fat is the number of a BBD block */
462
	BLP bbd     = GET_BBD_LIST (f, blk/(BB_BLOCK_SIZE/4));
463 464
	return        MS_OLE_GET_GUINT32 (BB_R_PTR(f,bbd)
					  + 4*(blk%(BB_BLOCK_SIZE/4)));
465 466
}

467
/* Builds the FAT */
468
static int
Michael Meeks's avatar
Michael Meeks committed
469
read_bb (MsOle *f)
470
{
471 472 473 474 475
	/* FIXME tenix may be later we wish to split this function */
	guint32  numbbd;
	BLP      lp;
	guint32  num_add_bbd_lists;
	BLP      missing_lps;
476
	BLP      missing_bbds;
477
	guint32  visited_add_bbd_list;
478 479
	BLP tmp;
	BLP bbd;
480

481 482 483
	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->mem, 0);

484
	f->bb   = g_array_new (FALSE, FALSE, sizeof(BLP));
485 486 487
	numbbd  = GET_NUM_BBD_BLOCKS  (f);

        /* Sanity checks */
488
/* FIXME tenix reading big files
489 490 491
	if (numbbd < ((f->length - BB_BLOCK_SIZE
		      + ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4) - 1)
			 / ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4))) {
492 493 494
		printf ("Duff block descriptors\n");
		return 0;
	}
495
 */
496
	/* FIXME tenix check if size is small, there's no add bbd lists */
497
	
498
	/* Add BBD's that live in the BBD list */
499 500
	for (lp = 0; (lp<(f->length/BB_BLOCK_SIZE)-1)
		      &&(lp<MAX_SIZE_BBD_LIST*BB_BLOCK_SIZE/4); lp++) {
501
		tmp = get_next_block (f, lp);
502
		g_array_append_val (f->bb, tmp);
503
	}
504

505 506 507 508
	/* Add BBD's that live in the additional BBD lists */
	num_add_bbd_lists = GET_NUM_ADD_BBD_LISTS (f);
	if (num_add_bbd_lists > 0) {
		/* FIXME change g_assert and return a error to user */
Michael Meeks's avatar
Michael Meeks committed
509
		g_assert (lp == MAX_SIZE_BBD_LIST*BB_BLOCK_SIZE/4);
510
		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
511 512 513 514 515
		missing_lps = (f->length/BB_BLOCK_SIZE) - 1 
			       - MAX_SIZE_BBD_LIST*BB_BLOCK_SIZE/4;
		for (lp = 0; lp < missing_lps; lp++) {
			if ((lp!=0) && !(lp%(MAX_SIZE_ADD_BBD_LIST*
					     (BB_BLOCK_SIZE/4)))) {
516 517
				/* This lp lives in the next add bbd list */
				visited_add_bbd_list = MS_OLE_GET_GUINT32(
518 519
						BB_R_PTR(f,visited_add_bbd_list)
						+4*MAX_SIZE_ADD_BBD_LIST);
520 521 522 523 524 525
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
			}
526 527 528 529 530 531 532

			/* tmp here means the number of one block that
			   belongs to the fat */
			bbd = MS_OLE_GET_GUINT32 (BB_R_PTR (f, visited_add_bbd_list) + 4*((lp/(BB_BLOCK_SIZE/4))%MAX_SIZE_ADD_BBD_LIST));
			tmp = MS_OLE_GET_GUINT32 (BB_R_PTR(f,bbd)
						  + 4*(lp%(BB_BLOCK_SIZE/4)));
			g_array_append_val (f->bb, tmp);
533
		}
534 535
		/* FIXME tenix do we check if we have visited all lp's but
		   there are more additional lists? */
536 537
	}

538 539 540 541 542
	/* Mark the bbd list blocks as unused */
	for (lp=0; lp < MIN (numbbd, MAX_SIZE_BBD_LIST); lp++) {
		TRY_MARK_UNUSED_BLOCK (f->bb, GET_BBD_LIST(f,lp),
				       SPECIAL_BLOCK);
	}
543 544
	if (num_add_bbd_lists > 0) {
		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
545 546 547 548 549
		TRY_MARK_UNUSED_BLOCK (f->bb, visited_add_bbd_list,
				      ADD_BBD_LIST_BLOCK);
		missing_bbds = numbbd - MAX_SIZE_BBD_LIST;
		for (lp = 0; lp < missing_bbds; lp++) {
			if ((lp!=0) && !(lp % (MAX_SIZE_ADD_BBD_LIST))) {
550 551 552
				/* This lp lives in the next add bbd list */
				visited_add_bbd_list = MS_OLE_GET_GUINT32(
						BB_R_PTR(f,visited_add_bbd_list)
553
						+ 4*MAX_SIZE_ADD_BBD_LIST);
554 555 556 557 558
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
559 560 561
				TRY_MARK_UNUSED_BLOCK (f->bb,
						       visited_add_bbd_list,
						       ADD_BBD_LIST_BLOCK);
562
			}
563 564 565

			bbd = MS_OLE_GET_GUINT32 (BB_R_PTR(f, visited_add_bbd_list) + 4*(lp%MAX_SIZE_ADD_BBD_LIST));
			TRY_MARK_UNUSED_BLOCK (f->bb, bbd, SPECIAL_BLOCK);
566 567
		}
	}
568

569
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
570 571
	/* FIXME tenix better check?:
	   g_assert (f->bb->len == f->length/BB_BLOCK_SIZE - 1); */
572 573

	/* More sanity checks */
574 575
/* FIXME
	for (lp=0; lp<numbbd; lp++) {
576
		BLP bbdblk = GET_BBD_LIST(f, lp);
577
		if (g_array_index(f->bb, BLP, bbdblk) != SPECIAL_BLOCK) {
578
			printf ("Error - BBD blocks not marked correctly\n");
579
			g_array_free (f->bb, TRUE);
580
			return 0;
581
		}
582 583
		}
*/
584 585 586 587 588

#if OLE_DEBUG > 1
	dump_header (f);
#endif
	return 1;
589 590
}

Michael Meeks's avatar
Michael Meeks committed
591
static void
Michael Meeks's avatar
Michael Meeks committed
592
extend_file (MsOle *f, guint blocks)
Michael Meeks's avatar
Michael Meeks committed
593
{
594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626
	if (!f->ole_mmap) {
		BBBlkAttr *s;
		guint32 blkidx, i;
		
		if (f->bbattr->len) {
			s = g_ptr_array_index (f->bbattr, f->bbattr->len-1);
			blkidx = s->blk+1;
		} else
			blkidx = 0;
		
		for (i=0;i<blocks;i++) {
			g_ptr_array_add (f->bbattr, bb_blk_attr_new (blkidx++));
			f->length+= BB_BLOCK_SIZE;
		}
	} else {
		struct stat st;
		int file;
		guint8 *newptr, zero = 0;
		guint32 oldlen;
		
		g_assert (f);
		file = f->file_des;
		
		g_assert (munmap(f->mem, f->length) != -1);
		/* Extend that file by blocks */
		
		if ((fstat(file, &st) == -1) ||
		    (lseek (file, st.st_size + BB_BLOCK_SIZE*blocks - 1, SEEK_SET)==(off_t)-1) ||
		    (write (file, &zero, 1) == -1)) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}
Michael Meeks's avatar
Michael Meeks committed
627

628 629 630 631 632 633 634
		oldlen = st.st_size;
		fstat(file, &st);
		f->length = st.st_size;
		g_assert (f->length == BB_BLOCK_SIZE*blocks + oldlen);
		if (f->length%BB_BLOCK_SIZE)
			printf ("Warning file %d non-integer number of blocks\n", f->length);
		newptr = mmap (f->mem, f->length, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
Michael Meeks's avatar
Michael Meeks committed
635
#if OLE_DEBUG > 0
636 637 638 639 640 641 642 643
		if (newptr != f->mem)
			printf ("Memory map moved from %p to %p\n",
				f->mem, newptr);
		
		if (newptr == MAP_FAILED) {
			f->mem = 0;
			g_warning ("panic: re-map failed!");
		}
Michael Meeks's avatar
Michael Meeks committed
644
#endif
645 646
		f->mem = newptr;
	}
Michael Meeks's avatar
Michael Meeks committed
647 648 649
}

static BLP
Michael Meeks's avatar
Michael Meeks committed
650
next_free_bb (MsOle *f)
Michael Meeks's avatar
Michael Meeks committed
651 652 653 654 655 656
{
	BLP blk, tblk;
  
	g_assert (f);

	blk = 0;
657
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
Michael Meeks's avatar
Michael Meeks committed
658 659 660 661 662 663
	while (blk < f->bb->len)
		if (g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK)
			return blk;
	        else 
			blk++;

664
	extend_file (f, 1);
Michael Meeks's avatar
Michael Meeks committed
665 666 667
	tblk = UNUSED_BLOCK;
	g_array_append_val (f->bb, tblk);
	g_assert ((g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK));
668
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
Michael Meeks's avatar
Michael Meeks committed
669 670 671 672
	return blk;
}

static int
Michael Meeks's avatar
Michael Meeks committed
673
write_bb (MsOle *f)
Michael Meeks's avatar
Michael Meeks committed
674 675
{
	guint32 numbbd;
676
	BLP     lp, lpblk;
Michael Meeks's avatar
Michael Meeks committed
677 678 679 680 681

	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->mem, 0);
	g_return_val_if_fail (f->bb,  0);

682 683 684
	numbbd  = f->bb->len/(BB_BLOCK_SIZE/4);
	if (f->bb->len%(BB_BLOCK_SIZE/4))
		numbbd++;
Michael Meeks's avatar
Michael Meeks committed
685 686 687 688 689 690 691 692 693 694
	SET_NUM_BBD_BLOCKS (f, numbbd);

	for (lp=0;lp<numbbd;lp++) {
		BLP blk = next_free_bb(f);
		SET_BBD_LIST (f, lp, blk);
		g_array_index (f->bb, BLP, blk) = SPECIAL_BLOCK;
	}

	lpblk = 0;
	while (lpblk<f->bb->len) { /* Described blocks */
695
		guint8 *mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
696
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
Michael Meeks's avatar
Michael Meeks committed
697 698 699 700
			     g_array_index (f->bb, BLP, lpblk));
		lpblk++;
	}
	while (lpblk%(BB_BLOCK_SIZE/4) != 0) { /* Undescribed blocks */
701 702
		guint8 *mem;
		g_assert (lpblk/(BB_BLOCK_SIZE/4) < numbbd);
703
		mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
704
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
Michael Meeks's avatar
Michael Meeks committed
705 706 707 708 709 710 711 712 713
			     UNUSED_BLOCK);
		lpblk++;
	}
	g_array_free (f->bb, TRUE);
	f->bb = 0;
	return 1;
}

static BLP
Michael Meeks's avatar
Michael Meeks committed
714
next_free_sb (MsOle *f)
Michael Meeks's avatar
Michael Meeks committed
715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734
{
	BLP blk, tblk;
  
	g_assert (f);

	blk = 0;
	while (blk < f->sb->len)
		if (g_array_index (f->sb, BLP, blk) == UNUSED_BLOCK)
			return blk;
	        else 
			blk++;
	
	tblk = UNUSED_BLOCK;
	g_array_append_val (f->sb, tblk);
	g_assert ((g_array_index (f->sb, BLP, blk) == UNUSED_BLOCK));
	g_assert (blk < f->sb->len);

	if ((f->sb->len + (BB_BLOCK_SIZE/SB_BLOCK_SIZE) - 1) / (BB_BLOCK_SIZE/SB_BLOCK_SIZE) >= f->sbf->len) {
	/* Create an extra big block on the small block stream */
		BLP new_sbf = next_free_bb(f);
735 736 737
		if (f->sbf->len > 0)
			g_array_index (f->bb, BLP, 
				       g_array_index (f->sbf, BLP, f->sbf->len-1)) = new_sbf;
Michael Meeks's avatar
Michael Meeks committed
738 739 740 741 742 743 744 745 746
		g_array_append_val (f->sbf, new_sbf);
		g_array_index (f->bb, BLP, new_sbf) = END_OF_CHAIN;
	}

	g_assert ((f->sb->len + (BB_BLOCK_SIZE/SB_BLOCK_SIZE) - 1) / (BB_BLOCK_SIZE/SB_BLOCK_SIZE) <= f->sbf->len);

	return blk;
}

747
static guint8 *
Michael Meeks's avatar
Michael Meeks committed
748
get_pps_ptr (MsOle *f, PPS_IDX i, gboolean forwrite)
749
{
750 751
	int lp;
	BLP blk = GET_ROOT_STARTBLOCK (f);
752

753 754 755 756 757 758 759 760
	lp = i/(BB_BLOCK_SIZE/PPS_BLOCK_SIZE);
	while (lp && blk != END_OF_CHAIN) {
		if (blk == SPECIAL_BLOCK ||
		    blk == UNUSED_BLOCK) {
			printf ("Duff block in root chain\n");
			return 0;
		}
		lp--;
761
		blk = NEXT_BB (f, blk);
762 763 764 765 766
	}
	if (blk == END_OF_CHAIN) {
		printf ("Serious error finding pps %d\n", i);
		return 0;
	}
767 768 769 770 771

	if (forwrite)
		return BB_W_PTR(f, blk) + (i%(BB_BLOCK_SIZE/PPS_BLOCK_SIZE))*PPS_BLOCK_SIZE;
	else
		return BB_R_PTR(f, blk) + (i%(BB_BLOCK_SIZE/PPS_BLOCK_SIZE))*PPS_BLOCK_SIZE;
772 773 774 775 776 777 778 779 780 781
}

static gint
pps_compare_func (PPS *a, PPS *b)
{
	g_return_val_if_fail (a, 0);
	g_return_val_if_fail (b, 0);
	g_return_val_if_fail (a->name, 0);
	g_return_val_if_fail (b->name, 0);
	
782
	return g_strcasecmp (b->name, a->name);
783 784 785
}

static void
Michael Meeks's avatar
Michael Meeks committed
786
pps_decode_tree (MsOle *f, PPS_IDX p, PPS *parent)
787
{
788
	PPS    *pps;
789 790 791 792 793 794
	guint8 *mem;
       
	if (p == PPS_END_OF_CHAIN)
		return;

	pps           = g_new (PPS, 1);
Michael Meeks's avatar
Michael Meeks committed
795
	pps->sig      = PPS_SIG;
796
	mem           = get_pps_ptr (f, p, FALSE);
797 798 799 800 801
	if (!mem) {
		printf ("Serious directory error %d\n", p);
		f->pps = NULL;
		return;
	}
802 803 804 805 806
	pps->name     = pps_get_text  (mem, PPS_GET_NAME_LEN(mem));
	pps->type     = PPS_GET_TYPE  (mem);
	pps->size     = PPS_GET_SIZE  (mem);
	pps->children = NULL;
	pps->parent   = parent;
807
	pps->idx      = 0;
808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827
	if (!pps->name) { /* Make safe */
		printf ("how odd: blank named file in directory\n");
		g_free (pps);
		return;
	}

	f->num_pps++;
	
	if (parent) {
#if OLE_DEBUG > 0
		printf ("Inserting '%s' into '%s'\n", pps->name, parent->name);
#endif
		parent->children = g_list_insert_sorted (parent->children, pps,
							 (GCompareFunc)pps_compare_func);
	}
	else {
#if OLE_DEBUG > 0
		printf ("Setting root to '%s'\n", pps->name);
#endif
		f->pps = g_list_append (0, pps);
Michael Meeks's avatar
Michael Meeks committed
828
	}
829 830 831 832 833 834 835 836 837 838 839 840

	if (PPS_GET_NEXT(mem) != PPS_END_OF_CHAIN)
		pps_decode_tree (f, PPS_GET_NEXT(mem), parent);
		
	if (PPS_GET_PREV(mem) != PPS_END_OF_CHAIN)
		pps_decode_tree (f, PPS_GET_PREV(mem), parent);

	if (PPS_GET_DIR (mem) != PPS_END_OF_CHAIN)
		pps_decode_tree (f, PPS_GET_DIR(mem), pps);

	pps->start   = PPS_GET_STARTBLOCK (mem);
	
841 842 843 844
#if OLE_DEBUG > 1
	printf ("PPS decode : '%s'\n", pps->name?pps->name:"Null");
	dump (mem, PPS_BLOCK_SIZE);
#endif
845 846 847 848
	return;
}

static int
Michael Meeks's avatar
Michael Meeks committed
849
read_pps (MsOle *f)
850
{
851
	PPS *pps;
852 853 854
	g_return_val_if_fail (f, 0);

	f->num_pps = 0;
855
	pps_decode_tree (f, PPS_ROOT_INDEX, NULL);
856

857
	if (!f->pps || g_list_length (f->pps) < 1 ||
858 859 860 861 862 863 864 865
	    g_list_length (f->pps) > 1) {
		printf ("Invalid root chain\n");
		return 0;
	} else if (!f->pps->data) {
		printf ("No root entry\n");
		return 0;
	}

866 867 868 869 870 871 872
	/* Fiddle root, perhaps our get_text is broken */
	/* perhaps it is just an MS oddity in coding */
	pps = f->pps->data;
	if (pps->name)
		g_free (pps->name);
	pps->name = g_strdup ("Root Entry");

873 874 875 876 877
	{ /* Free up the root chain */
		BLP blk, last;
		last = blk = GET_ROOT_STARTBLOCK (f);
		while (blk != END_OF_CHAIN) {
			last = blk;
878
			blk = NEXT_BB (f, blk);
879 880 881 882 883 884 885 886 887
			g_array_index (f->bb, BLP, last) = UNUSED_BLOCK;
		}
	}
	
	if (!f->pps) {
		printf ("Root directory too small\n");
		return 0;
	}
	return 1;
888 889
}

890 891 892
/**
 * Write the blocks main data recursively.
 **/
Michael Meeks's avatar
Michael Meeks committed
893
static void
Michael Meeks's avatar
Michael Meeks committed
894
pps_encode_tree_initial (MsOle *f, GList *list, PPS_IDX *p)
Michael Meeks's avatar
Michael Meeks committed
895
{
Michael Meeks's avatar
Michael Meeks committed
896
	int lp, max;
897 898
	guint8 *mem;
	PPS    *pps;
Michael Meeks's avatar
Michael Meeks committed
899

900 901 902 903
	g_return_if_fail (list);
	g_return_if_fail (list->data);
	
	pps = list->data;
904 905 906 907 908

#if OLE_DEBUG > 0
	printf ("encoding '%s' as %d\n", pps->name, pps->idx);
#endif

909
	pps->idx = *p;
910
	(*p)++;
911
	mem = get_pps_ptr (f, pps->idx, TRUE);
912

Michael Meeks's avatar
Michael Meeks committed
913 914
	/* Blank stuff I don't understand */
	for (lp=0;lp<PPS_BLOCK_SIZE;lp++)
915
		MS_OLE_SET_GUINT8(mem+lp, 0);
Michael Meeks's avatar
Michael Meeks committed
916 917 918 919 920
	if (pps->name) {
		max = strlen (pps->name);
		if (max >= (PPS_BLOCK_SIZE/4))
			max = (PPS_BLOCK_SIZE/4);
		for (lp=0;lp<max;lp++)
921
			MS_OLE_SET_GUINT16(mem + lp*2, pps->name[lp]);
Michael Meeks's avatar
Michael Meeks committed
922
	} else {
923
		printf ("No name %d\n", *p);
Michael Meeks's avatar
Michael Meeks committed
924 925 926
		max = -1;
	}
	PPS_SET_NAME_LEN(mem, (max+1)*2);
927 928
	
	/* Magic numbers */
929
	if (pps->idx == PPS_ROOT_INDEX) { /* Only Root */
930 931 932 933
		MS_OLE_SET_GUINT32  (mem + 0x50, 0x00020900);
		MS_OLE_SET_GUINT32  (mem + 0x58, 0x000000c0);
		MS_OLE_SET_GUINT32  (mem + 0x5c, 0x46000000);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x01); /* or zero ? */
934
	} else if (pps->size >= BB_THRESHOLD) {
935 936
		MS_OLE_SET_GUINT32  (mem + 0x50, 0x00020900);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x01);
937
	} else {
938 939 940
		MS_OLE_SET_GUINT32  (mem + 0x64, 0x09299c3c);
		MS_OLE_SET_GUINT32  (mem + 0x6c, 0x09299c3c);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x00);
941
	}
942 943 944

	PPS_SET_TYPE (mem, pps->type);
	PPS_SET_SIZE (mem, pps->size);
Michael Meeks's avatar
Michael Meeks committed
945
        PPS_SET_STARTBLOCK(mem, pps->start);
946 947 948
	PPS_SET_NEXT (mem, PPS_END_OF_CHAIN);
	PPS_SET_PREV (mem, PPS_END_OF_CHAIN);
	PPS_SET_DIR  (mem, PPS_END_OF_CHAIN);
Michael Meeks's avatar
Michael Meeks committed
949

Michael Meeks's avatar
Michael Meeks committed
950
#if MsOle_DEBUG > 1
951 952 953 954
	printf ("Encode '%s' as \n", pps->name);
	dump (mem, PPS_BLOCK_SIZE);
#endif

955 956 957 958 959 960 961 962 963 964 965
	if (pps->children)
		pps_encode_tree_initial (f, pps->children, p);
	if (g_list_next (list))
		pps_encode_tree_initial (f, g_list_next(list), p);
}

/**
 * Chain the blocks together afterwards
 * FIXME: Leaks like a sieve
 **/
static void
Michael Meeks's avatar
Michael Meeks committed
966
pps_encode_tree_chain (MsOle *f, GList *list)
967 968 969 970 971 972 973 974 975 976 977
{
	PPS     *pps, *p;
	GList   *l;
	int      lp, len;
	PPS     *next, *prev;
	guint8  *mem, *parmem;

	g_return_if_fail (list);
	g_return_if_fail (list->data);
	
	pps      = list->data;
978
	parmem   = get_pps_ptr (f, pps->idx, TRUE);
979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 1001 1002 1003 1004 1005 1006 1007 1008 1009
	g_return_if_fail (pps->children);
	len      = g_list_length (pps->children);
	l        = pps->children;

	if (len==0) {
#if OLE_DEBUG > 0
		printf ("Empty directory '%s'\n", pps->name);
		return;
#endif		
	} else if (len==1) {
		p = l->data;
		PPS_SET_DIR  (parmem, p->idx);
		return;
	}

	g_assert (l);
	next = prev = p = l->data;

#if OLE_DEBUG > 0
	printf ("No. of entries is %d\n", len);
#endif
	if (len/2==1)
		l = g_list_next (l);

	for (lp=1;lp<len/2;lp++) {
		p    = l->data;
		prev = g_list_previous(l)->data;

#if OLE_DEBUG > 0
		printf ("Chaining previous for '%s'\n", p->name);
#endif
Michael Meeks's avatar
Michael Meeks committed
1010
		if (p->type == MsOleStorageT)
1011 1012
			pps_encode_tree_chain (f, l);

1013
		mem  = get_pps_ptr (f, p->idx, TRUE);
1014 1015 1016 1017 1018 1019 1020 1021 1022 1023 1024 1025 1026 1027 1028 1029 1030
		PPS_SET_NEXT (mem, PPS_END_OF_CHAIN);
		PPS_SET_PREV (mem, prev->idx);
		l    = g_list_next (l);
	}

	g_assert (l);
	prev   = p;
	p      = l->data;

	/* The base node of the directory */
	PPS_SET_DIR  (parmem, p->idx);

#if OLE_DEBUG > 0
	printf ("Base node is '%s'\n", p->name);
#endif

	/* Points potentialy both ways */
1031
	mem    = get_pps_ptr (f, p->idx, TRUE);
1032 1033 1034 1035 1036 1037 1038 1039 1040 1041 1042 1043 1044 1045
	PPS_SET_PREV (mem, prev->idx);
	l      = g_list_next (l);
	if (l)
		PPS_SET_NEXT (mem, ((PPS *)l->data)->idx);
	else
		PPS_SET_NEXT (mem, PPS_END_OF_CHAIN);

	while (l && g_list_next(l)) {
	        p    = l->data;
		next = g_list_next (l)->data;

#if OLE_DEBUG > 0
		printf ("Chaining next for '%s'\n", p->name);
#endif
Michael Meeks's avatar
Michael Meeks committed
1046
		if (p->type == MsOleStorageT)
1047 1048
			pps_encode_tree_chain (f, l);

1049
		mem  = get_pps_ptr (f, p->idx, TRUE);
1050 1051 1052 1053
		PPS_SET_NEXT (mem, next->idx);
		PPS_SET_PREV (mem, PPS_END_OF_CHAIN);
		l = g_list_next (l);
	}
Michael Meeks's avatar
Michael Meeks committed
1054 1055 1056
}

static int
Michael Meeks's avatar
Michael Meeks committed
1057
write_pps (MsOle *f)
Michael Meeks's avatar
Michael Meeks committed
1058
{
1059 1060
	int lp;
	PPS_IDX idx;
Michael Meeks's avatar
Michael Meeks committed
1061 1062 1063
	BLP blk  = END_OF_CHAIN;
	BLP last = END_OF_CHAIN;

1064 1065 1066 1067 1068 1069 1070 1071
	/* Build the root chain */
	for (lp=0;lp<(f->num_pps+(BB_BLOCK_SIZE/PPS_BLOCK_SIZE)-1)/(BB_BLOCK_SIZE/PPS_BLOCK_SIZE);lp++) {
		last  = blk;
		blk   = next_free_bb (f);
		g_assert (g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK);
		if (last != END_OF_CHAIN)
			g_array_index (f->bb, BLP, last) = blk;
		else {
Michael Meeks's avatar
Michael Meeks committed
1072
#if OLE_DEBUG > 0
1073
			printf ("Set root block to %d\n", blk);