[CMF-checkins] CVS: CMF - config.h:1.1 glibconfig.h:1.1 libole2.h:1.1 ms-ole-summary.c:1.1 ms-ole-summary.h:1.1 ms-ole-vba.c:1.1 ms-ole-vba.h:1.1 ms-ole.c:1.1 ms-ole.h:1.1 ole.c:1.1 ole.i:1.1 ole_wrap.c:1.1 setup.py:1.1 test-ole.c:1.1 version.c:1.1

jack@digicool.com jack@digicool.com
Wed, 30 May 2001 10:39:21 -0400 (EDT)


Update of /cvs-repository/Packages/Products/DCProject/CMF_MS_Files/src/libole
In directory korak.digicool.com:/tmp/cvs-serv12283

Added Files:
	config.h glibconfig.h libole2.h ms-ole-summary.c 
	ms-ole-summary.h ms-ole-vba.c ms-ole-vba.h ms-ole.c ms-ole.h 
	ole.c ole.i ole_wrap.c setup.py test-ole.c version.c 
Log Message:
Adding CMF_MS_Files Product



--- Added File config.h in package Packages/Products/DCProject ---
/* config.h.  Generated automatically by configure.  */
#ifndef CONFIG_H_CALLED
#define CONFIG_H_CALLED 1

/* config.h.in.  Generated automatically from configure.in by autoheader.  */

/* Define to empty if the keyword does not work.  */
/* #undef const */

/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
/* #undef HAVE_SYS_WAIT_H */

/* Define if you have the wait3 system call.  */
/* #undef HAVE_WAIT3 */

/* Define if you have the waitpid system call.  */
/* #undef HAVE_WAITPID */

/* Define as the return type of signal handlers (int or void).  */
/* #undef RETSIGTYPE */

/* Define if you have the <errno.h> header file.  */
#define HAVE_ERRNO_H 1

/* #undef HAVE_POSIX_SIGNALS */

/* Define to `int' if <sys/types.h> doesn't define.  */
/* #undef pid_t */

/* Define if you have the ANSI C header files.  */
#define STDC_HEADERS 1

/* Define if you have the <fcntl.h> header file.  */
#define HAVE_FCNTL_H 1

/* Define if you have the <sys/file.h> header file.  */
#define HAVE_SYS_FILE_H 1

/* Define if you have the <sys/ioctl.h> header file.  */
#define HAVE_SYS_IOCTL_H 1

/* Define if you have the <unistd.h> header file.  */
#define HAVE_UNISTD_H 1

/* Define if you want zlib to uncompress wmf files */
/* #undef SYSTEM_ZLIB */

/* Define if you have libwmf and want it to convert wmf to gif files */
#define HAVE_WMF 1

/* Define if you have freetype*/
#define HAVE_TTF 1

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#if defined(HAVE_ERRNO_H)
#include <errno.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
/* #include <dirent.h> */
#include <stdarg.h>

/*
#include <getopt.h>
*/

#define MATCHED_TYPE 1
/* #undef WORDS_BIGENDIAN */
#define XML_BYTE_ORDER 12

#if defined(__GNUC__) && !defined(WORDS_BIGENDIAN) && defined(MATCHED_TYPE)
#define NO_HOLES
#endif

/* define if you have iconv */
#define USE_ICONV 1

/* define if you have iconv but do not have windows codepage to unicode support */
/* #undef MUST_USE_INTERNAL_ICONV_TABLE */

/* define if you have libpng */
#define HasPNG 1

/* Define if you have the memcpy function.  */
#define HAVE_MEMCPY 1

#ifndef HAVE_MEMCPY
#define memcpy(d, s, n) bcopy ((s), (d), (n))
#endif /* not HAVE_MEMCPY */

#define HAVE_MMAP 1

#endif /* CONFIG_H_CALLED */

--- Added File glibconfig.h in package Packages/Products/DCProject ---
/* glibconfig.h
 *
 * This is a generated file.  Please modify `configure.in'
 */

#ifndef GLIBCONFIG_H
#define GLIBCONFIG_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <limits.h>
#include <float.h>
#define GLIB_HAVE_SYS_POLL_H

#define G_MINFLOAT	FLT_MIN
#define G_MAXFLOAT	FLT_MAX
#define G_MINDOUBLE	DBL_MIN
#define G_MAXDOUBLE	DBL_MAX
#define G_MINSHORT	SHRT_MIN
#define G_MAXSHORT	SHRT_MAX
#define G_MININT	INT_MIN
#define G_MAXINT	INT_MAX
#define G_MINLONG	LONG_MIN
#define G_MAXLONG	LONG_MAX

typedef signed char gint8;
typedef unsigned char guint8;
typedef signed short gint16;
typedef unsigned short guint16;
typedef signed int gint32;
typedef unsigned int guint32;

#if defined (__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define G_GNUC_EXTENSION __extension__
#else
#  define G_GNUC_EXTENSION
#endif

#define G_HAVE_GINT64 1

G_GNUC_EXTENSION typedef signed long long gint64;
G_GNUC_EXTENSION typedef unsigned long long guint64;

#define G_GINT64_CONSTANT(val)	(G_GNUC_EXTENSION (val##LL))

#define GPOINTER_TO_INT(p)	((gint)   (p))
#define GPOINTER_TO_UINT(p)	((guint)  (p))

#define GINT_TO_POINTER(i)	((gpointer)  (i))
#define GUINT_TO_POINTER(u)	((gpointer)  (u))

#ifdef NeXT /* @#%@! NeXTStep */
# define g_ATEXIT(proc)	(!atexit (proc))
#else
# define g_ATEXIT(proc)	(atexit (proc))
#endif

#define g_memmove(d,s,n) G_STMT_START { memmove ((d), (s), (n)); } G_STMT_END

#define GLIB_MAJOR_VERSION 1
#define GLIB_MINOR_VERSION 2
#define GLIB_MICRO_VERSION 8


#define G_VA_COPY	__va_copy

#ifdef	__cplusplus
#define	G_HAVE_INLINE	1
#else	/* !__cplusplus */
#define G_HAVE_INLINE 1
#define G_HAVE___INLINE 1
#define G_HAVE___INLINE__ 1
#endif	/* !__cplusplus */

#define G_THREADS_ENABLED
#define G_THREADS_IMPL_POSIX
typedef struct _GStaticMutex GStaticMutex;
struct _GStaticMutex
{
  struct _GMutex *runtime_mutex;
  union {
    char   pad[24];
    double dummy_double;
    void  *dummy_pointer;
    long   dummy_long;
  } aligned_pad_u;
};
#define	G_STATIC_MUTEX_INIT	{ NULL, { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} } }
#define	g_static_mutex_get_mutex(mutex)   (g_thread_use_default_impl ? ((GMutex*) &((mutex)->aligned_pad_u)) :    g_static_mutex_get_mutex_impl (&((mutex)->runtime_mutex)))

#define GINT16_TO_LE(val)	((gint16) (val))
#define GUINT16_TO_LE(val)	((guint16) (val))
#define GINT16_TO_BE(val)	((gint16) GUINT16_SWAP_LE_BE (val))
#define GUINT16_TO_BE(val)	(GUINT16_SWAP_LE_BE (val))
#define GINT32_TO_LE(val)	((gint32) (val))
#define GUINT32_TO_LE(val)	((guint32) (val))
#define GINT32_TO_BE(val)	((gint32) GUINT32_SWAP_LE_BE (val))
#define GUINT32_TO_BE(val)	(GUINT32_SWAP_LE_BE (val))
#define GINT64_TO_LE(val)	((gint64) (val))
#define GUINT64_TO_LE(val)	((guint64) (val))
#define GINT64_TO_BE(val)	((gint64) GUINT64_SWAP_LE_BE (val))
#define GUINT64_TO_BE(val)	(GUINT64_SWAP_LE_BE (val))
#define GLONG_TO_LE(val)	((glong) GINT32_TO_LE (val))
#define GULONG_TO_LE(val)	((gulong) GUINT32_TO_LE (val))
#define GLONG_TO_BE(val)	((glong) GINT32_TO_BE (val))
#define GULONG_TO_BE(val)	((gulong) GUINT32_TO_BE (val))
#define GINT_TO_LE(val)		((gint) GINT32_TO_LE (val))
#define GUINT_TO_LE(val)	((guint) GUINT32_TO_LE (val))
#define GINT_TO_BE(val)		((gint) GINT32_TO_BE (val))
#define GUINT_TO_BE(val)	((guint) GUINT32_TO_BE (val))
#define G_BYTE_ORDER G_LITTLE_ENDIAN

#define GLIB_SYSDEF_POLLIN =1
#define GLIB_SYSDEF_POLLOUT =4
#define GLIB_SYSDEF_POLLPRI =2
#define GLIB_SYSDEF_POLLERR =8
#define GLIB_SYSDEF_POLLHUP =16
#define GLIB_SYSDEF_POLLNVAL =32


#define G_HAVE_WCHAR_H 1
#define G_HAVE_WCTYPE_H 1


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* GLIBCONFIG_H */

--- Added File libole2.h in package Packages/Products/DCProject ---
#include "ms-ole.h"
#include "ms-ole-summary.h"

extern int libole2_major_version;
extern int libole2_minor_version;
extern int libole2_micro_version;

#define LIBOLE2_MAJOR_VERSION 0
#define LIBOLE2_MINOR_VERSION 1
#define LIBOLE2_MICRO_VERSION 7

--- Added File ms-ole-summary.c in package Packages/Products/DCProject ---
/**
 * ms-ole-summary.c: MS Office OLE support
 *
 * Authors:
 *    Michael Meeks (mmeeks@gnu.org)
 *    Frank Chiulli (fc-linux@home.com)
 * From work by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 * Built on work by:
 *    Somar Software's CPPSUM (http://www.somar.com)
 **/

#include <config.h>
#include <glib.h>
#include <stdio.h>

#include <ms-ole.h>
#include <ms-ole-summary.h>


#define SUMMARY_ID(x) ((x) & 0xff)

typedef struct {
	guint32             offset;
	guint32             id;
	MsOlePropertySetID  ps_id;
} item_t;

const guint32	sum_fmtid[4] =  {
				 0xF29F85E0,
		           	 0x10684FF9,
		           	 0x000891AB,
		           	 0xD9B3272B
				};

const guint32	doc_fmtid[4] =  {	
				 0xD5CDD502,
		           	 0x101B2E9C,
		           	 0x00089793,
		           	 0xAEF92C2B
			        };


const guint32	user_fmtid[4] = {			
				 0XD5CDD505,
				 0X101B2E9C,
		           	 0X00089793,
		           	 0XAEF92C2B
				};


static gboolean
read_items (MsOleSummary *si, MsOlePropertySetID ps_id)
{
	gint sect;
	
	for (sect = 0; sect < si->sections->len; sect++) {
		MsOleSummarySection st;
		guint8 data[8];
		gint   i;
		
		st = g_array_index (si->sections, MsOleSummarySection, sect);

		if (st.ps_id != ps_id)
			continue;

		si->s->lseek (si->s, st.offset, MsOleSeekSet);
		if (!si->s->read_copy (si->s, data, 8))
			return FALSE;
		
		st.bytes = MS_OLE_GET_GUINT32 (data);
		st.props = MS_OLE_GET_GUINT32 (data + 4);

		if (st.props == 0)
			continue;
		
		for (i = 0; i < st.props; i++) {
			item_t item;
			if (!si->s->read_copy (si->s, data, 8))
				return FALSE;

			item.id     = MS_OLE_GET_GUINT32 (data);
			item.offset = MS_OLE_GET_GUINT32 (data + 4);
			item.offset = item.offset + st.offset;
			item.ps_id  = ps_id;
			g_array_append_val (si->items, item);
		}
	}
	return TRUE;
}

typedef struct {
	MsOleSummaryPID  id;
	guint32          len;
	guint8          *data;
} write_item_t;


#define PROPERTY_HDR_LEN	8
#define PROPERTY_DESC_LEN	8
static void
write_items (MsOleSummary *si)
{
	MsOlePos cur_pos;
	MsOlePos pos = 48; /* magic offset see: _create_stream */
	guint8   data[PROPERTY_DESC_LEN];
	guint8   fill_data[] = {0, 0, 0, 0};
	guint32  i, num;
	guint32  offset = 0;
	GList   *l;

	/*
	 *  Write out the property descriptors.
	 *  Keep track of the number of properties and number of bytes for the properties.
	 */
	si->s->lseek (si->s, pos + PROPERTY_HDR_LEN, MsOleSeekSet);

	l = si->write_items;
	num = g_list_length (l);
	i = 0;
	offset = PROPERTY_HDR_LEN + num * PROPERTY_DESC_LEN;
	while (l) {
		write_item_t *w = l->data;
		g_return_if_fail (w != NULL);

		/*
		 *  The offset is calculated from the start of the 
		 *  properties header.  The offset must be on a 
		 *  4-byte boundary.  Therefore all data written must be
		 *  in multiples of 4-bytes.
		 */
		MS_OLE_SET_GUINT32 (data + 0, w->id & 0xff);
		MS_OLE_SET_GUINT32 (data + 4, offset);
		si->s->write (si->s, data, PROPERTY_DESC_LEN);

		offset += w->len;
		if ((w->len & 0x3) > 0)
			offset += (4 - (w->len & 0x3));
			
		i++;

		l = g_list_next (l);
	}

	g_return_if_fail (i == num);
	
	/*
	 *  Write out the section header.
	 */
	si->s->lseek (si->s, pos, MsOleSeekSet);
	MS_OLE_SET_GUINT32 (data + 0, offset);
	MS_OLE_SET_GUINT32 (data + 4, i);
	si->s->write (si->s, data, PROPERTY_HDR_LEN);

	/*
	 *  Write out the property values.
	 *  Keep track of the last position written to.
	 */
	cur_pos = pos + PROPERTY_HDR_LEN + num*PROPERTY_DESC_LEN;
	si->s->lseek (si->s, cur_pos, MsOleSeekSet);
	l = si->write_items;
	while (l) {
		write_item_t *w = l->data;
		si->s->write (si->s, w->data, w->len);
		cur_pos += w->len;
		l = g_list_next (l);

		/*
		 * Write out any fill.
		 */
		if ((w->len & 0x3) > 0) {
		        cur_pos += (4 - (w->len & 0x3));
			si->s->write (si->s, fill_data, 4 - (w->len & 0x3));
		}

	}

	/*
	 * Pad it out to a BB file.
	 */
	{
		int     i;
		for (i = cur_pos; i < 0x1000; i+=4)
			si->s->write (si->s, fill_data, 4);
	}

}

/**
 * ms_ole_summary_open_stream:
 * @stream: stream object
 * @psid: Property Set ID, indicates which property set to open
 * 
 * Opens @s as a summary stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * Summary Stream.
 **/
MsOleSummary *
ms_ole_summary_open_stream (MsOleStream *stream,
			    const MsOlePropertySetID psid)
{
	guint8              data[64];
	guint16             byte_order;
	gboolean            panic = FALSE;
	guint32             os_version;
	MsOleSummary       *si;
	gint                i, sections;

	g_return_val_if_fail (stream != NULL, NULL);

	if (!stream->read_copy (stream, data, 28))
		return NULL;

	si                = g_new (MsOleSummary, 1);

	si->s             = stream;
	si->write_items   = NULL;
	si->sections      = NULL;
	si->items         = NULL;
	si->read_mode     = TRUE;

	byte_order        = MS_OLE_GET_GUINT16(data);
	if (byte_order != 0xfffe)
		panic     = TRUE;

	if (MS_OLE_GET_GUINT16 (data + 2) != 0) /* Format */
		panic     = TRUE;

	os_version        = MS_OLE_GET_GUINT32 (data + 4);

	for (i = 0; i < 16; i++)
		si->class_id[i] = data[8 + i];

	sections          = MS_OLE_GET_GUINT32 (data + 24);

	if (panic) {
		ms_ole_summary_close (si);
		return NULL;
	}

	si->sections = g_array_new (FALSE, FALSE, sizeof (MsOleSummarySection));

	for (i = 0; i < sections; i++) {
		MsOleSummarySection sect;
		if (!stream->read_copy (stream, data, 16 + 4)) {
			ms_ole_summary_close (si);
			return NULL;
		}
		
		if (psid == MS_OLE_PS_SUMMARY_INFO) {
			if (MS_OLE_GET_GUINT32 (data +  0) == sum_fmtid[0] &&
			    MS_OLE_GET_GUINT32 (data +  4) == sum_fmtid[1] &&
			    MS_OLE_GET_GUINT32 (data +  8) == sum_fmtid[2] &&
			    MS_OLE_GET_GUINT32 (data + 12) == sum_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_SUMMARY_INFO;
			
			} else {
				ms_ole_summary_close (si);
				return NULL;
			}
			
		} else if (psid == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) {
			if (MS_OLE_GET_GUINT32 (data +  0) == doc_fmtid[0] &&
		            MS_OLE_GET_GUINT32 (data +  4) == doc_fmtid[1] &&
		            MS_OLE_GET_GUINT32 (data +  8) == doc_fmtid[2] &&
		            MS_OLE_GET_GUINT32 (data + 12) == doc_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
			
			} else if (MS_OLE_GET_GUINT32 (data +  0) == user_fmtid[0] &&
		            	   MS_OLE_GET_GUINT32 (data +  4) == user_fmtid[1] &&
				   MS_OLE_GET_GUINT32 (data +  8) == user_fmtid[2] &&
				   MS_OLE_GET_GUINT32 (data + 12) == user_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_USER_DEFINED_SUMMARY_INFO;
			
			} else {
				ms_ole_summary_close (si);
				return NULL;
			}
		}

		sect.offset = MS_OLE_GET_GUINT32 (data + 16);
		g_array_append_val (si->sections, sect);
		/* We want to read the offsets of the items here into si->items */
	}

	si->items = g_array_new (FALSE, FALSE, sizeof (item_t));

	for (i = 0; i < sections; i++) {
		MsOleSummarySection st;

		st = g_array_index (si->sections, MsOleSummarySection, i);
		if (!read_items (si, st.ps_id)) {
			// JPP g_warning ("Serious error reading items");
			ms_ole_summary_close (si);
			return NULL;
		}
	}

	return si;
}

/**
 * ms_ole_summary_open:
 * @f: filesystem object.
 * 
 * Opens the SummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * SummaryInformation Stream.
 **/
MsOleSummary *
ms_ole_summary_open (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;
	
	// JPP g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05SummaryInformation", 'r');
	if (result != MS_OLE_ERR_OK || !s)
		return NULL;

	return ms_ole_summary_open_stream (s, MS_OLE_PS_SUMMARY_INFO);
}


/**
 * ms_ole_docsummary_open:
 * @f: filesystem object.
 * 
 * Opens the DocumentSummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * DocumentSummaryInformation Stream.
 **/
MsOleSummary *
ms_ole_docsummary_open (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;
	
	// JPP g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
	                             "\05DocumentSummaryInformation", 'r');
	if (result != MS_OLE_ERR_OK || !s)
		return NULL;

	return ms_ole_summary_open_stream (s, MS_OLE_PS_DOCUMENT_SUMMARY_INFO);
}


/*
 * Cheat by hard coding magic numbers and chaining on.
 */
/**
 * ms_ole_summary_create_stream:
 * @s: stream object
 * @psid: Property Set ID, indicates which property set to open
 * 
 * Creates @s as a summary stream (@psid determines which one), returns NULL on
 * failure.
 * 
 * Return value: %NULL if unable to create stream, otherwise a pointer to a new
 * summary stream.
 **/
MsOleSummary *
ms_ole_summary_create_stream (MsOleStream *s, const MsOlePropertySetID psid)
{
	guint8        data[78];
	MsOleSummary *si;
	g_return_val_if_fail (s != NULL, NULL);

	MS_OLE_SET_GUINT16 (data +  0, 0xfffe); /* byte order */
	MS_OLE_SET_GUINT16 (data +  2, 0x0000); /* format */
	MS_OLE_SET_GUINT16 (data +  4, 0x0001); /* OS version A */
	MS_OLE_SET_GUINT16 (data +  6, 0x0000); /* OS version B */

	MS_OLE_SET_GUINT32 (data +  8, 0x0000); /* class id */
	MS_OLE_SET_GUINT32 (data + 12, 0x0000);
	MS_OLE_SET_GUINT32 (data + 16, 0x0000);
	MS_OLE_SET_GUINT32 (data + 20, 0x0000);

	if (psid == MS_OLE_PS_SUMMARY_INFO) {
		MS_OLE_SET_GUINT32 (data + 24, 0x0001); /* Sections */

		MS_OLE_SET_GUINT32 (data + 28, sum_fmtid[0]); /* ID */
		MS_OLE_SET_GUINT32 (data + 32, sum_fmtid[1]);
		MS_OLE_SET_GUINT32 (data + 36, sum_fmtid[2]);
		MS_OLE_SET_GUINT32 (data + 40, sum_fmtid[3]);

		MS_OLE_SET_GUINT32 (data + 44, 0x30); /* Section offset = 48 */

		MS_OLE_SET_GUINT32 (data + 48,  0); /* bytes */
		MS_OLE_SET_GUINT32 (data + 52,  0); /* properties */

		s->write (s, data, 56);

	} else if (psid == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) {
		MS_OLE_SET_GUINT32 (data + 24, 0x0001); /* Sections */

		MS_OLE_SET_GUINT32 (data + 28, doc_fmtid[0]); /* ID */
		MS_OLE_SET_GUINT32 (data + 32, doc_fmtid[1]);
		MS_OLE_SET_GUINT32 (data + 36, doc_fmtid[2]);
		MS_OLE_SET_GUINT32 (data + 40, doc_fmtid[3]);

		MS_OLE_SET_GUINT32 (data + 44, 0x30); /* Section offset = 48 */

		MS_OLE_SET_GUINT32 (data + 48,  0); /* bytes */
		MS_OLE_SET_GUINT32 (data + 52,  0); /* properties */

		s->write (s, data, 56);

	}

	s->lseek (s, 0, MsOleSeekSet);

	si = ms_ole_summary_open_stream (s, psid);
	si->read_mode = FALSE;

	return si;
}


/**
 * ms_ole_summary_create:
 * @f: filesystem object.
 * 
 * Create a SummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to create the stream, otherwise a pointer to a
 * new SummaryInformation stream.
 **/
MsOleSummary *
ms_ole_summary_create (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;

	g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05SummaryInformation", 'w');
	if (result != MS_OLE_ERR_OK || !s) {
		printf ("ms_ole_summary_create: Can't open stream for writing\n");
		return NULL;
	}

	return ms_ole_summary_create_stream (s, MS_OLE_PS_SUMMARY_INFO);
}


/**
 * ms_ole_docsummary_create:
 * @f: filesystem object.
 * 
 * Create a DocumentSummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to create the stream, otherwise a pointer to a
 * new DocumentSummaryInformation stream.
 **/
MsOleSummary *
ms_ole_docsummary_create (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;

	g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05DocumentSummaryInformation", 'w');
	if (result != MS_OLE_ERR_OK || !s) {
		printf ("ms_ole_docsummary_create: Can't open stream for writing\n");
		return NULL;
	}

	return ms_ole_summary_create_stream (s, MS_OLE_PS_DOCUMENT_SUMMARY_INFO);
}


/* FIXME: without the helpful type */
/**
 * ms_ole_summary_get_properties:
 * @si: summary stream
 * 
 * Returns an array of MsOleSummaryPID.
 * 
 * Return value: an array of property ids in the current summary stream or 
 * %NULL if either the summary stream is non-existent or the summary stream
 * contains no properties.
 **/
GArray *
ms_ole_summary_get_properties (MsOleSummary *si)
{
	GArray *ans;
	gint i;

	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (si->items != NULL, NULL);

	ans = g_array_new (FALSE, FALSE, sizeof (MsOleSummaryPID));
	g_array_set_size  (ans, si->items->len);
	for (i = 0; i < si->items->len; i++)
		g_array_index (ans, MsOleSummaryPID, i) = 
			g_array_index (si->items, item_t, i).id;

	return ans;
}

/**
 * ms_ole_summary_close:
 * @si: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_close (MsOleSummary *si)
{
	g_return_if_fail (si != NULL);
	g_return_if_fail (si->s != NULL);

	if (!si->read_mode)
		write_items (si);
	
	if (si->sections)
		g_array_free (si->sections, TRUE);
	si->sections = NULL;

	if (si->items)
		g_array_free (si->items, TRUE);
	si->items = NULL;

	if (si->s)
		ms_ole_stream_close (&si->s);
	si->s = NULL;

	g_free (si);
}


/*
 *                        Record handling code
 */
#define TYPE_SHORT	0x02		/*   2, VT_I2,		2-byte signed integer  */
#define TYPE_LONG       0x03		/*   3, VT_I4,		4-byte signed integer  */
#define TYPE_BOOLEAN	0x0b		/*  11, VT_BOOL,	Boolean value  */
#define TYPE_STRING     0x1e		/*  30, VT_LPSTR,	Pointer to null terminated ANSI string */
#define TYPE_TIME       0x40		/*  64, VT_FILETIME,	64-bit FILETIME structure  */
#define TYPE_PREVIEW    0x47		/*  71, VT_CF,		Pointer to a CLIPDATA structure  */

/* Seeks to the correct place, and returns a handle or NULL on failure */
static item_t *
seek_to_record (MsOleSummary *si, MsOleSummaryPID id)
{
	gint i;
	g_return_val_if_fail (si->items, FALSE);

	/* These should / could be sorted for speed */
	for (i = 0; i < si->items->len; i++) {
		item_t item = g_array_index (si->items, item_t, i);
		if (item.id == SUMMARY_ID(id)) {
			gboolean is_summary, is_doc_summary;

			is_summary     = ((si->ps_id == MS_OLE_PS_SUMMARY_INFO) && 
					  (item.ps_id == MS_OLE_PS_SUMMARY_INFO));
			is_doc_summary = ((si->ps_id == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) && 
					  (item.ps_id == MS_OLE_PS_DOCUMENT_SUMMARY_INFO));
			if (is_summary || is_doc_summary) {
				si->s->lseek (si->s, item.offset, MsOleSeekSet);
				return &g_array_index (si->items, item_t, i);
			}
		}
	}
	return NULL;
}

/**
 * ms_ole_summary_get_string:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * Note: Ensure that you free returned value after use.
 * 
 * Return value: FIXME
 **/
char *
ms_ole_summary_get_string (MsOleSummary *si, MsOleSummaryPID id,
			   gboolean *available)
{
	guint8   data[8];
	guint32  type, len;
	gchar   *ans;
	item_t *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (si->read_mode, NULL);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_STRING, NULL);

	if (!(item = seek_to_record (si, id)))
		return NULL;

	if (!si->s->read_copy (si->s, data, 8))
		return NULL;

	type = MS_OLE_GET_GUINT32 (data);
	len  = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_STRING) { /* Very odd */
		// JPP g_warning ("Summary string type mismatch");
		return NULL;
	}

	ans = g_new (gchar, len + 1);
	
	if (!si->s->read_copy (si->s, ans, len)) {
		g_free (ans);
		return NULL;
	}

	ans[len] = '\0';

	*available = TRUE;
	return ans;
}

/**
 * ms_ole_summary_get_short:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
guint16
ms_ole_summary_get_short (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8   data[8];
	guint32  type;
	guint32  value;
	item_t  *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_SHORT, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32 (data);
	value = MS_OLE_GET_GUINT16 (data + 4);

	if (type != TYPE_SHORT) { /* Very odd */
		// JPP g_warning ("Summary short type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}

/**
 * ms_ole_summary_get_boolean:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
gboolean
ms_ole_summary_get_boolean (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean *available)
{
	guint8    data[8];
	guint32   type;
	gboolean  value;
	item_t   *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_BOOLEAN, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32  (data);
	value = MS_OLE_GET_GUINT16 (data + 4);

	if (type != TYPE_BOOLEAN) { /* Very odd */
		// JPP g_warning ("Summary boolean type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}

/**
 * ms_ole_summary_get_long:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
guint32
ms_ole_summary_get_long (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8  data[8];
	guint32 type, value;
	item_t *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_LONG, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32 (data);
	value = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_LONG) { /* Very odd */
		// JPP g_warning ("Summary long type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}


/*
 *  filetime_to_unixtime
 *
 *  Convert a FILETIME format to unixtime
 *  FILETIME is the number of 100ns units since January 1, 1601.
 *  unixtime is the number of seconds since January 1, 1970.
 *
 *  The difference in 100ns units between the two dates is:
 *	116,444,736,000,000,000  (TIMEDIF)
 *  (I'll let you do the math)
 *  If we divide this into pieces,
 *    high 32-bits = 27111902 or  TIMEDIF / 16^8
 *    mid  16-bits =    54590 or (TIMEDIF - (high 32-bits * 16^8)) / 16^4
 *    low  16-bits =    32768 or (TIMEDIF - (high 32-bits * 16^8) - (mid 16-bits * 16^4)
 *
 *  where all math is integer.
 *
 *  Adapted from work in 'wv' by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 */
#define HIGH32_DELTA	27111902
#define MID16_DELTA	   54590
#define LOW16_DELTA        32768

/**
 * filetime_to_unixtime:
 * @low_time: FIXME
 * @high_time: FIXME
 * 
 * Converts a FILETIME format to unixtime. FILETIME is the number of 100ns units
 * since January 1, 1601. unixtime is the number of seconds since January 1,
 * 1970.
 * 
 * Return value: FIXME
 **/
glong filetime_to_unixtime (guint32 low_time, guint32 high_time);
glong
filetime_to_unixtime (guint32 low_time, guint32 high_time)
{
	guint32		 low16;		/* 16 bit, low    bits */
	guint32		 mid16;		/* 16 bit, medium bits */
	guint32		 hi32;		/* 32 bit, high   bits */
	unsigned int	 carry;		/* carry bit for subtraction */
	int		 negative;	/* whether a represents a negative value */

	/* Copy the time values to hi32/mid16/low16 */
	hi32  =  high_time;
	mid16 = low_time >> 16;
	low16 = low_time &  0xffff;

	/* Subtract the time difference */
	if (low16 >= LOW16_DELTA           )
		low16 -=             LOW16_DELTA        , carry = 0;
	else
		low16 += (1 << 16) - LOW16_DELTA        , carry = 1;

	if (mid16 >= MID16_DELTA    + carry)
		mid16 -=             MID16_DELTA + carry, carry = 0;
	else
		mid16 += (1 << 16) - MID16_DELTA - carry, carry = 1;

	hi32 -= HIGH32_DELTA + carry;

	/* If a is negative, replace a by (-1-a) */
	negative = (hi32 >= ((guint32)1) << 31);
	if (negative) {
		/* Set a to -a - 1 (a is hi32/mid16/low16) */
		low16 = 0xffff - low16;
		mid16 = 0xffff - mid16;
		hi32 = ~hi32;
	}

	/*
	 *  Divide a by 10000000 (a = hi32/mid16/low16), put the rest into r.
         * Split the divisor into 10000 * 1000 which are both less than 0xffff.
	 */
	mid16 += (hi32 % 10000) << 16;
	hi32  /=       10000;
	low16 += (mid16 % 10000) << 16;
	mid16 /=       10000;
	low16 /=       10000;

	mid16 += (hi32 % 1000) << 16;
	hi32  /=       1000;
	low16 += (mid16 % 1000) << 16;
	mid16 /=       1000;
	low16 /=       1000;

	/* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
	if (negative) {
		/* Set a to -a - 1 (a is hi32/mid16/low16) */
		low16 = 0xffff - low16;
		mid16 = 0xffff - mid16;
		hi32 = ~hi32;
	}

	/*  Do not replace this by << 32, it gives a compiler warning and 
	 *  it does not work
	 */
	return ((((glong)hi32) << 16) << 16) + (mid16 << 16) + low16;

}


/**
 * unixtime_to_filetime:
 * @unix_time: FIXME
 * @time_high: FIXME
 * @time_low: FIXME
 * 
 * Converts a unixtime format to FILETIME. FILETIME is the number of 100ns units
 * since January 1, 1601. unixtime is the number of seconds since January 1,
 * 1970.
 **/
void unixtime_to_filetime (time_t unix_time, unsigned int *time_high,
			   unsigned int *time_low);
void
unixtime_to_filetime (time_t unix_time, unsigned int *time_high, unsigned int *time_low)
{
	unsigned int	 low_16;
	unsigned int	 mid_16;
	unsigned int	 high32;
	unsigned int	 carry;
	
	/*
	 *  First split unix_time up.
	 */
	high32 = (unix_time >> 16) >> 16;
	mid_16 =  unix_time >> 16;
	low_16 =  unix_time  & 0xffff;
	
	/*
	 *  Convert seconds to 100 ns units by multipling by 10,000,000.
	 *  Do this in two steps, 10,000 and 1,000.
	 */
	low_16 *= 10000;
	carry   = (low_16) >> 16;
	low_16  = low_16 & 0xffff;
	
	mid_16 *= 10000;
	mid_16 += carry;
	carry   = (mid_16 >> 16);
	mid_16  = mid_16 & 0xffff;
	
	high32 *= 10000;
	high32 += carry;
	
	
	low_16 *= 1000;
	carry   = (low_16) >> 16;
	low_16  = low_16 & 0xffff;
	
	mid_16 *= 1000;
	mid_16 += carry;
	carry   = (mid_16 >> 16);
	mid_16  = mid_16 & 0xffff;
	
	high32 *= 1000;
	high32 += carry;
	
	/*
	 *  Now add in the time difference.
	 */
	low_16 += LOW16_DELTA;
	mid_16 += (low_16 >> 16);
	low_16  =  low_16  & 0xffff;
	
	mid_16 += MID16_DELTA;
	high32 += (mid_16 >> 16);
	mid_16  =  mid_16  & 0xffff;
	
	high32 += HIGH32_DELTA;
	
	*time_high = high32;
	*time_low  = (mid_16 << 16) + low_16;
	
	return;
	
}


/**
 * ms_ole_summary_get_time:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
GTimeVal
ms_ole_summary_get_time (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8   data[12];
	guint32  type;
	guint32  low_time;
	guint32  high_time;
	item_t  *item;
	GTimeVal time;

	time.tv_sec  = 0;   /* Magic numbers */
	time.tv_usec = 0;
/*	g_date_set_dmy (&time.date, 18, 6, 1977); */

	g_return_val_if_fail (available != NULL, time);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, time);
	g_return_val_if_fail (si->read_mode, time);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_TIME, time);

	if (!(item = seek_to_record (si, id)))
		return time;

	if (!si->s->read_copy (si->s, data, 12))
		return time;

	type      = MS_OLE_GET_GUINT32 (data);
	low_time  = MS_OLE_GET_GUINT32 (data + 4);
	high_time = MS_OLE_GET_GUINT32 (data + 8);

	if (type != TYPE_TIME) { /* Very odd */
		// JPP g_warning ("Summary time type mismatch");
		return time;
	}

	time.tv_sec = filetime_to_unixtime (low_time, high_time);
	
	*available = TRUE;
	return time;
}

/**
 * ms_ole_summary_preview_destroy:
 * @d: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_preview_destroy (MsOleSummaryPreview d)
{
	if (d.data)
		g_free (d.data);
	d.data = NULL;
}

/**
 * ms_ole_summary_get_preview:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
MsOleSummaryPreview
ms_ole_summary_get_preview (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean *available)
{
	guint8  data[8];
	guint32 type;
	MsOleSummaryPreview ans;
	item_t *item;

	ans.len  = 0;
	ans.data = NULL;

	g_return_val_if_fail (available != NULL, ans);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, ans);
	g_return_val_if_fail (si->read_mode, ans);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_OTHER, ans);

	if (!(item = seek_to_record (si, id)))
		return ans;

	if (!si->s->read_copy (si->s, data, 8))
		return ans;

	type     = MS_OLE_GET_GUINT32 (data);
	ans.len  = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_PREVIEW) { /* Very odd */
		// JPP g_warning ("Summary wmf type mismatch");
		return ans;
	}

	ans.data = g_new (guint8, ans.len + 1);
	
	if (!si->s->read_copy (si->s, ans.data, ans.len)) {
		g_free (ans.data);
		return ans;
	}

	*available = TRUE;
	return ans;
}

static write_item_t *
write_item_t_new (MsOleSummary *si, MsOleSummaryPID id)
{
	write_item_t *w = g_new (write_item_t, 1);

	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (!si->read_mode, NULL);

	w->id           = id;
	w->len          = 0;
	w->data         = NULL;
	si->write_items = g_list_append (si->write_items, w);

	return w;
}

/**
 * ms_ole_summary_set_preview:
 * @si: FIXME
 * @id: FIXME
 * @preview: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_preview (MsOleSummary *si, MsOleSummaryPID id,
			    const MsOleSummaryPreview *preview)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);
	g_return_if_fail (preview != NULL);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, preview->len + 8);

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_PREVIEW);
	MS_OLE_SET_GUINT32 (w->data + 4, preview->len);

	memcpy (w->data + 8, preview->data, preview->len);
	
	w->len = preview->len + 8;
}

/**
 * ms_ole_summary_set_time:
 * @si: FIXME
 * @id: FIXME
 * @time: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_time (MsOleSummary *si, MsOleSummaryPID id,
			 GTimeVal time)
{
	unsigned int	 time_high;
	unsigned int	 time_low;
	write_item_t	*w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 12);
	w->len  = 12;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_TIME);
	
        unixtime_to_filetime ((time_t)time.tv_sec, &time_high, &time_low);
	
	MS_OLE_SET_GUINT32 (w->data + 4, time_low);
	MS_OLE_SET_GUINT32 (w->data + 8, time_high);
}

/**
 * ms_ole_summary_set_boolean:
 * @si: FIXME
 * @id: FIXME
 * @bool: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_boolean (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean value)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 6;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_BOOLEAN);
	MS_OLE_SET_GUINT16 (w->data + 4, value);
}



/**
 * ms_ole_summary_set_short:
 * @si: FIXME
 * @id: FIXME
 * @i: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_short (MsOleSummary *si, MsOleSummaryPID id,
			  guint16 i)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 6;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_SHORT);
	MS_OLE_SET_GUINT16 (w->data + 4, i);
}

/**
 * ms_ole_summary_set_long:
 * @si: FIXME
 * @id: FIXME
 * @i: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_long (MsOleSummary *si, MsOleSummaryPID id,
			 guint32 i)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 8;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_LONG);
	MS_OLE_SET_GUINT32 (w->data + 4, i);
}

/**
 * ms_ole_summary_set_string:
 * @si: FIXME
 * @id: FIXME
 * @str: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_string (MsOleSummary *si, MsOleSummaryPID id,
			   const gchar *str)
{
	write_item_t *w;
	guint32 len;

	g_return_if_fail (si != NULL);
	g_return_if_fail (str != NULL);
	g_return_if_fail (!si->read_mode);

	w       = write_item_t_new (si, id);
	len     = strlen (str) + 1;
	w->len  = len + 8;
	w->data = g_new (guint8, len + 8);
	
	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_STRING);
	MS_OLE_SET_GUINT32 (w->data + 4, len);

	memcpy (w->data + 8, str, len);
}

--- Added File ms-ole-summary.h in package Packages/Products/DCProject ---
/**
 * ms-ole-summary.h: MS Office OLE support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 * From work by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 * Built on work by:
 *    Somar Software's CPPSUM (http://www.somar.com)
 **/

#ifndef MS_OLE_SUMMARY_H
#define MS_OLE_SUMMARY_H

#include <time.h>
#include <ms-ole.h>

/*
 * MS Ole Property Set IDs
 * The SummaryInformation stream contains the SummaryInformation property set.
 * The DocumentSummaryInformation stream contains both the
 * DocumentSummaryInformation and the UserDefined property sets as sections.
 */
typedef enum {
	MS_OLE_PS_SUMMARY_INFO,
	MS_OLE_PS_DOCUMENT_SUMMARY_INFO,
	MS_OLE_PS_USER_DEFINED_SUMMARY_INFO
} MsOlePropertySetID;

typedef struct {
	guint8			class_id[16];
	GArray *		sections;
	GArray *		items;
	GList *			write_items;
	gboolean		read_mode;
	MsOleStream *		s;
	MsOlePropertySetID	ps_id;
} MsOleSummary;

/* Could store the FID, but why bother ? */
typedef struct {
	guint32			offset;
	guint32			props;
	guint32			bytes;
	MsOlePropertySetID	ps_id;
} MsOleSummarySection;

MsOleSummary *ms_ole_summary_open		(MsOle *f);
MsOleSummary *ms_ole_docsummary_open		(MsOle *f);
MsOleSummary *ms_ole_summary_open_stream	(MsOleStream *stream,
						 const MsOlePropertySetID psid);
MsOleSummary *ms_ole_summary_create		(MsOle *f);
MsOleSummary *ms_ole_docsummary_create		(MsOle *f);
MsOleSummary *ms_ole_summary_create_stream	(MsOleStream *s,
						 const MsOlePropertySetID psid);
GArray       *ms_ole_summary_get_properties	(MsOleSummary *si);
void	      ms_ole_summary_close		(MsOleSummary *si);


/*
 * Can be used to interrogate a summary item as to its type
 */
typedef enum {
	MS_OLE_SUMMARY_TYPE_STRING  = 0x10,
	MS_OLE_SUMMARY_TYPE_TIME    = 0x20,
	MS_OLE_SUMMARY_TYPE_LONG    = 0x30,
	MS_OLE_SUMMARY_TYPE_SHORT   = 0x40,
	MS_OLE_SUMMARY_TYPE_BOOLEAN = 0x50,
	MS_OLE_SUMMARY_TYPE_OTHER   = 0x60
} MsOleSummaryType;

#define MS_OLE_SUMMARY_TYPE(x) ((MsOleSummaryType)((x)>>8))

/* FIXME MS_OLE_SUMMARY_THUMBNAIL is Preview, no Security, isn't it? */
/*
 *  The MS byte specifies the type, the LS byte is the
 * 'standard' MS PID.
 */
typedef enum {
/* SummaryInformation Stream Properties */
/* String properties */
	MS_OLE_SUMMARY_TITLE          = 0x1002,
	MS_OLE_SUMMARY_SUBJECT        = 0x1003,
	MS_OLE_SUMMARY_AUTHOR         = 0x1004,
	MS_OLE_SUMMARY_KEYWORDS       = 0x1005,
	MS_OLE_SUMMARY_COMMENTS       = 0x1006,
	MS_OLE_SUMMARY_TEMPLATE       = 0x1007,
	MS_OLE_SUMMARY_LASTAUTHOR     = 0x1008,
	MS_OLE_SUMMARY_REVNUMBER      = 0x1009,
	MS_OLE_SUMMARY_APPNAME        = 0x1012,
	
/* Time properties */
	MS_OLE_SUMMARY_TOTAL_EDITTIME = 0x200A,
	MS_OLE_SUMMARY_LASTPRINTED    = 0x200B,
	MS_OLE_SUMMARY_CREATED        = 0x200C,
	MS_OLE_SUMMARY_LASTSAVED      = 0x200D,
	
/* Long integer properties */
	MS_OLE_SUMMARY_PAGECOUNT      = 0x300E,
	MS_OLE_SUMMARY_WORDCOUNT      = 0x300F,
	MS_OLE_SUMMARY_CHARCOUNT      = 0x3010,
	MS_OLE_SUMMARY_SECURITY       = 0x3013,

/* Short integer properties */
	MS_OLE_SUMMARY_CODEPAGE       = 0x4001,

/* Security */	
	MS_OLE_SUMMARY_THUMBNAIL      = 0x6011,


/* DocumentSummaryInformation Properties */
/* String properties */
	MS_OLE_SUMMARY_CATEGORY	      = 0x1002,
	MS_OLE_SUMMARY_PRESFORMAT     = 0x1003,
	MS_OLE_SUMMARY_MANAGER        = 0x100E,
	MS_OLE_SUMMARY_COMPANY        = 0x100F,

/* Long integer properties */
	MS_OLE_SUMMARY_BYTECOUNT      = 0x3004,
	MS_OLE_SUMMARY_LINECOUNT      = 0x3005,
	MS_OLE_SUMMARY_PARCOUNT       = 0x3006,
	MS_OLE_SUMMARY_SLIDECOUNT     = 0x3007,
	MS_OLE_SUMMARY_NOTECOUNT      = 0x3008,
	MS_OLE_SUMMARY_HIDDENCOUNT    = 0x3009,
	MS_OLE_SUMMARY_MMCLIPCOUNT    = 0X300A,

/* Boolean properties */
	MS_OLE_SUMMARY_SCALE          = 0x500B,
	MS_OLE_SUMMARY_LINKSDIRTY     = 0x5010
} MsOleSummaryPID;


/* bit masks for security long integer */
#define MsOleSummaryAllSecurityFlagsEqNone        0x00
#define MsOleSummarySecurityPassworded            0x01
#define MsOleSummarySecurityRORecommended         0x02
#define MsOleSummarySecurityRO                    0x04
#define MsOleSummarySecurityLockedForAnnotations  0x08

typedef struct {
	GTimeVal time;
	GDate    date;
} MsOleSummaryTime;

typedef struct {
	guint32 len;
	guint8 *data;
} MsOleSummaryPreview;

gchar *			ms_ole_summary_get_string	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
gboolean		ms_ole_summary_get_boolean	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
guint16			ms_ole_summary_get_short	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
guint32			ms_ole_summary_get_long		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
GTimeVal		ms_ole_summary_get_time		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
MsOleSummaryPreview	ms_ole_summary_get_preview	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
void			ms_ole_summary_preview_destroy	(MsOleSummaryPreview d);

/* FIXME The next comment isn't true, is it?
   Return TRUE if write is successful */
void			ms_ole_summary_set_string	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 const gchar *str);
void			ms_ole_summary_set_boolean	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean value);
void			ms_ole_summary_set_short	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 guint16 i);
void			ms_ole_summary_set_long		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 guint32 i);
void			ms_ole_summary_set_time		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 GTimeVal time);
void			ms_ole_summary_set_preview	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 const
							 MsOleSummaryPreview *
							 preview);

#endif	/* MS_OLE_SUMMARY_H */


--- Added File ms-ole-vba.c in package Packages/Products/DCProject ---
/**
 * ms-ole-vba.c: MS Office VBA support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 *
 * Copyright 2000 Helix Code, Inc.
 **/

#include <config.h>
#include <stdio.h>

#include <ms-ole-vba.h>

#undef VBA_DEBUG


struct _MsOleVba {
	MsOleStream *s;
	GArray      *text;
	int          pos;
};

inline gboolean
ms_ole_vba_eof (MsOleVba *vba)
{
	return !vba || (vba->pos >= vba->text->len - 1);
}

char
ms_ole_vba_getc (MsOleVba *vba)
{
	g_assert (!ms_ole_vba_eof (vba));

	return g_array_index (vba->text, guint8, vba->pos++);
}

char
ms_ole_vba_peek (MsOleVba *vba)
{
	g_assert (!ms_ole_vba_eof (vba));

	return g_array_index (vba->text, guint8, vba->pos);
}

#if VBA_DEBUG > 1
static void
print_bin (guint16 dt)
{
	int i;
	
	printf ("|");
	for (i = 15; i >= 0; i--) {
		if (dt & (1 << i))
			printf ("1");
		else
			printf ("0");
		if (i == 8)
			printf ("|");
	}
	printf ("|");
}
#endif


/**
 * decompress_vba
 *   @vba	Place to store the uncompressed VBA
 *   @data	Pointer to start of compressed VBA
 *   @eos	lwa+1 of stream
 *
 * Purpose: lzw, arc like compression.
 *
 * Internal function.
 **/
static void
decompress_vba (MsOleVba *vba, guint8 *data, guint8 *eos)
{
#define BUF_SIZE    6144 /* a bottleneck */

	guint8	 buffer[BUF_SIZE];
	guint8  *ptr;
	guint8	*sptr;

	guint32	 len;
	guint32  pos;

	GArray  *ans = g_array_new (FALSE, FALSE, 1);


	vba->text = ans;
	vba->pos  = 0;

	len = MS_OLE_GET_GUINT16 (data + 1);

#if VBA_DEBUG > 0
	printf ("Length 0x%x\n", len);
#endif

	len  = (len & ~0xb000) + 1;
	ptr  = data + 3;
	sptr = ptr;
	pos  = 0;

	while (ptr < eos) {
#if VBA_DEBUG > 0
		printf ("My compressed stream (addr=%#x, len = %#x (%d)):\n", 
		        ptr - data, len, len);
		ms_ole_dump (ptr, len);
#endif

		while ((ptr < sptr + len) && (ptr < eos)) {
			int	 shift;

			guint8	 flag_byte = *ptr++;

			/*
			 * The first byte is a flag byte.  Each bit in this byte
			 * determines what the next byte is.  If the bit is zero,
			 * the next byte is a character.  Otherwise the  next two
			 * bytes contain the number of characters to copy from the
			 * umcompresed buffer and where to copy them from (offset,
			 * length).
			 */
			for (shift = 0x01; shift < 0x100; shift = shift << 1) {
				if (ptr >= sptr + len)
					break;

				if (pos == BUF_SIZE) {
#if VBA_DEBUG > 0
					printf ("\nSomething extremely odd"
						" happens after %d bytes 0x%x\n\n",
						BUF_SIZE, MS_OLE_GET_GUINT16 (ptr));
					ms_ole_dump (ptr, len - (ptr - data));
#endif
					ptr       += 2;
					flag_byte  = *ptr++;
					pos        = 0;
					shift      = 0x01;
				}

				if (flag_byte & shift) {
					int	 i;
					int	 back;
					int	 clen;
					int	 shft;

					guint16	 dt = MS_OLE_GET_GUINT16 (ptr);

					if (pos <= 16)
						shft = 12;

					else if (pos <= 32)
						shft = 11;

					else if (pos <= 64)
						shft = 10;

					else if (pos <= 128)
						shft = 9;

					else if (pos <= 256)
						shft = 8;

					else if (pos <= 512)
						shft = 7;

					else if (pos <= 1024)
						shft = 6;

					else if (pos <= 2048)
						shft = 5;

					else
						shft = 4;

					back = (dt >> shft) + 1;
					clen = 0;

					for (i = 0; i < shft; i++)
						clen |= dt & (0x1 << i);
					clen += 3;

#if VBA_DEBUG > 1
					printf ("|match 0x%x (%d,%d) >> %d = %d, %d| pos = %d |\n",
						dt, (dt>>8), (dt&0xff), shft, back, clen, pos);
 					/* Perhaps dt & SHIFT = dist. to end of run */
					print_bin (dt);
					printf ("\n");
#endif				
					for (i = 0; i < clen; i++) {
						guint8	 c;

						guint32	 srcpos = (BUF_SIZE + (pos%BUF_SIZE)) - 
								   back;
						
						if (srcpos >= BUF_SIZE)
							srcpos-= BUF_SIZE;

						g_assert (srcpos >= 0);
						g_assert (srcpos < BUF_SIZE);

						c = buffer [srcpos];
						buffer [pos++ % BUF_SIZE] = c;
						g_array_append_val (ans, c);
#if VBA_DEBUG > 0
						printf ("%c", c);
#endif
					}
					ptr += 2;

				} else {
					buffer [pos++ % BUF_SIZE] = *ptr;
					g_array_append_val (ans, *ptr);
#if VBA_DEBUG > 0
					printf ("%c", *ptr);
#endif
					ptr++;
				}

				if ((ptr >= sptr + len) || (ptr >= eos)) {
					if ((ptr >= sptr + len) && (ptr < eos)) {
#if VBA_DEBUG > 0
						printf ("Reseting ptr.\n");
						printf ("ptr was %#x\n",
						        ptr - sptr);
						printf ("ptr is  %#x\n",
						        len);
#endif
						ptr = sptr + len;
					}
					break;
				}
			}  /* for (shift = 0x01; shift < 0x100; shift = shift << 1) */

		}  /* while ((ptr < sptr + len) && (ptr < eos)) */

#if VBA_DEBUG > 0
		printf ("ptr = %#X\n", ptr-sptr);
#endif
		if ((ptr + 3) < eos) {
			len  = MS_OLE_GET_GUINT16 (ptr);
			len  = (len & ~0xb000) + 1;
			ptr += 2;
			sptr = ptr;
			pos  = 0;
		}

	}  /* while (ptr < eos) */
	
	{
		char c;

		c = '\n';
		g_array_append_val (ans, c);

		c = '\0';
		g_array_append_val (ans, c);
	}
}

static guint8 *
seek_sig (guint8 *data, int len)
{
	int i;
	guint8 vba_sig[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x1 };

	for (i = 0; i < len; i++) {
		guint8 *p = data;
		int j;

		for (j = 0; j < sizeof (vba_sig); j++) {
			if (*p++ != vba_sig [j])
				break;
		}
		if (j == sizeof (vba_sig))
			return p;

		data++;
	}

	return NULL;
}

static guint8 *
find_compressed_vba (guint8 *data, MsOlePos len)
{
	guint8	*sig;
	guint32  offset;
	guint32  offpos;
		
	if (!(sig = seek_sig (data, len))) {
		g_warning ("No VBA kludge signature");
		return NULL;
	}

	offpos = MS_OLE_GET_GUINT32 (sig) + 0xd0 - 0x6b - 0x8;

#if VBA_DEBUG > 0
	printf ("Offpos : 0x%x -> \n", offpos);
#endif

	offset = MS_OLE_GET_GUINT32 (sig + offpos);

	if (len < offset + 3) {
		g_warning ("Too small for offset 0x%x\n", offset);
		return NULL;
	}

#if VBA_DEBUG > 0
	printf ("Offset is 0x%x\n", offset);
#endif

	return data + offset;
}


/**
 * ms_ole_vba_open:
 * @s: the stream pointer.
 * 
 * Attempt to open a stream as a VBA stream, and commence
 * decompression of it.
 * 
 * Return value: NULL if not a VBA stream or fails.
 **/
MsOleVba *
ms_ole_vba_open (MsOleStream *s)
{
	const guint8	 gid [16] = { 0x1,  0x16, 0x1,  0x0,
				      0x6,  0xb6, 0x0,  0xff,
				      0xff, 0x1,  0x1,  0x0,
				      0x0,  0x0,  0x0,  0xff };
	
	int		 i;
	int		 j;
	int		 len;
	
	guint8      *data, *vba_data;
	guint8       sig [16];
	
	MsOleVba	*vba;


	g_return_val_if_fail (s != NULL, NULL);

	if (s->size < 16)
		return NULL;

	s->lseek     (s, 0, MsOleSeekSet);
	s->read_copy (s, sig, 16);

	for (i = 0; i < 16; i++)
		if (sig [i] != gid [i]) {
			/*
			 * Version ??
			*/
			if (i == 4)
			  if (sig [i] == 0x4 )
			  	continue;
			return NULL;
		}

	data = g_new (guint8, s->size);

	s->lseek (s, 0, MsOleSeekSet);
	if (!s->read_copy (s, data, s->size)) {
		g_warning ("Strange: failed read");
		g_free (data);		
		return NULL;
	}

	if (!(vba_data = find_compressed_vba (data, s->size))) {
		g_free (data);
		return NULL;
	}

	if (MS_OLE_GET_GUINT8 (vba_data) != 1)
		g_warning ("Digit 0x%x != 1...", MS_OLE_GET_GUINT8 (vba_data));

	vba      = g_new0 (MsOleVba, 1);
	vba->s   = s;
	vba->pos = 0;

	decompress_vba (vba, vba_data, data + s->size);
	g_free (data);
	
	return vba;
}

/**
 * me_ols_vba_close:
 * @vba: 
 * 
 *   Free the resources associated with this vba
 * stream.
 **/
void
ms_ole_vba_close (MsOleVba *vba)
{
	if (vba) {
		g_array_free (vba->text, TRUE);
		vba->text = NULL;

		g_free (vba);
	}
}


--- Added File ms-ole-vba.h in package Packages/Products/DCProject ---
/**
 * ms-ole-vba.h: MS Office VBA support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 *
 * Copyright 2000 Helix Code, Inc.
 **/

#ifndef MS_OLE_VBA_H
#define MS_OLE_VBA_H

#include <ms-ole.h>

typedef struct _MsOleVba MsOleVba;

MsOleVba *ms_ole_vba_open  (MsOleStream *s);
void      ms_ole_vba_close (MsOleVba    *vba);

char      ms_ole_vba_getc  (MsOleVba    *vba);
char      ms_ole_vba_peek  (MsOleVba    *vba);
gboolean  ms_ole_vba_eof   (MsOleVba    *vba);

#endif

--- Added File ms-ole.c in package Packages/Products/DCProject ---
/**
 * ms-ole.c: MS Office OLE support for Gnumeric
 *
 * Authors:
 *    Michael Meeks (michael@imaginator.com)
 *    Arturo Tena   (arturo@directmail.org)
 **/

#include <stdio.h>

/* BSDs require unistd.h before including stat.h */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>	/* for struct stat */
#include <fcntl.h>

#include <assert.h>
#include <ctype.h>
#include <glib.h>
#include <string.h>

#include <ms-ole.h>
#include "config.h"

#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif

#ifdef HAVE_UNISTD_H
#	include <unistd.h>
#else
#	include <io.h>
#	include <sys/stat.h>
#	include <sys/types.h>
/*
#	define S_IRUSR 0000400
#	define S_IWUSR 0000200
#	define S_IRGRP 0000040
#	define S_IWGRP 0000020
#	define _S_ISREG(m) (((m)&0170000) == 0100000)
#	define S_ISREG(m) _S_ISREG(m)
#	define O_NONBLOCK 0x4000
*/
#endif

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

/* Implementational detail - not for global header */
#define OLE_DEBUG 0

/* 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 */

/* FIXME tenix laola reads this from the header */
#define BB_BLOCK_SIZE     512
#define SB_BLOCK_SIZE      64

/* 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
 */

MsOle *jp_msOle;

/**
 * Structure describing an OLE file
 **/
struct _MsOle
{
	int               ref_count;
	gboolean          ole_mmap;
	guint8           *mem;
	guint32           length;
	MsOleSysWrappers *syswrap;
	
	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 */
/* if memory mapped */
	GPtrArray        *bbattr;  /* Pointers to block structures */
/* end if memory mapped */

	unsigned char *buf;
	size_t len;
	size_t idx;
};



/**
 * Default system calls wrappers
 **/

static int
open2_wrap (const char *pathname, int flags)
{
	return open (pathname, flags);
}

static int
open3_wrap (const char *pathname, int flags, mode_t mode)
{
	return open (pathname, flags, mode);
}

static ssize_t
read_wrap (int fd, void *buf, size_t count)
{
	// printf(":read_wrap:\n");
	return read (fd, buf, count);
}

static int
close_wrap (int fd)
{
	return close (fd);
}

static ssize_t
write_wrap (int fd, const void *buf, size_t count)
{
	return write (fd, buf, count);
}

static off_t
lseek_wrap (int fd, off_t offset, int whence)
{
	return lseek (fd, offset, whence);
}

static int
isregfile_wrap (int fd)
{
	struct stat st;

	if (fstat (fd, &st))
		return 0;

	return S_ISREG(st.st_mode);
}

static int
getfilesize_wrap (int fd, guint32 *size)
{
	struct stat st;

	if (fstat (fd, &st))
		return -1;

	*size = st.st_size;
	return 0;
}
static MsOleSysWrappers default_wrappers = {
	open2_wrap,
	open3_wrap,
	read_wrap,
	close_wrap,
	write_wrap,
	lseek_wrap,
	isregfile_wrap,	
	getfilesize_wrap
};

//----------------------------------------------------------------------------//

static int open2_wrap_body (const char *pathname, int flags)
{
	int fd;
	fd = 3;
	return fd;
}

//----------------------------------------------------------------------------//

static int open3_wrap_body (const char *pathname, int flags, mode_t mode)
{
	int fd;
	fd = 3;
	return fd;
}

//----------------------------------------------------------------------------//

static ssize_t read_wrap_body (int fd, void *buf, size_t count)
{
	ssize_t temp;
	// printf(":read_wrap_body:\n");
	temp = count;
	memcpy(buf, &(jp_msOle->buf[jp_msOle->idx]), count);
	jp_msOle->idx = jp_msOle->idx +count;
	return temp;
}

//----------------------------------------------------------------------------//

static int close_wrap_body (int fd)
{
	int result;
	result = 0;
	return result;
}

//----------------------------------------------------------------------------//

static ssize_t write_wrap_body (int fd, const void *buf, size_t count)
{
	ssize_t temp;
	temp = write(fd, buf, count);
	return temp;
}

//----------------------------------------------------------------------------//

static off_t lseek_wrap_body (int fd, off_t offset, int whence)
{
	off_t temp;
	temp = offset;	
	jp_msOle->idx = offset;
	return temp;
}

//----------------------------------------------------------------------------//

static int isregfile_wrap_body (int fd)
{
	int result;
	struct stat st;
	result = 1;
	return result;
}

//----------------------------------------------------------------------------//

static int getfilesize_wrap_body (int fd, guint32 *size)
{
	int result;
	struct stat st;
	result = jp_msOle->len;
	*size = result;
	return 0;
}

//----------------------------------------------------------------------------//

static MsOleSysWrappers default_wrappers_body = 
{
	open2_wrap_body,
	open3_wrap_body,
	read_wrap_body,
	close_wrap_body,
	write_wrap_body,
	lseek_wrap_body,
	isregfile_wrap_body,	
	getfilesize_wrap_body
};

//----------------------------------------------------------------------------//

static void
take_wrapper_functions (MsOle *f, MsOleSysWrappers *wrappers) {
	if (wrappers == NULL)
		f->syswrap = &default_wrappers;
	else
		f->syswrap = wrappers;
}


/*
 * 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;

typedef guint32 PPS_IDX ;

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

static guint32
my_array_hack (GArray *a, guint s, guint32 idx)
{
	g_assert (a != NULL);
	g_assert (idx >= 0);
	g_assert (idx < a->len);
	g_assert (s == 4);
	return ((guint32 *)a->data)[idx];
}
#else
/* Far far faster... */
#       define ms_array_index(a,b,c) g_array_index (a, b, c)
#endif


typedef guint32 BLP;	/* Block pointer */


#define BB_THRESHOLD   0x1000

#define PPS_ROOT_INDEX    0
#define PPS_BLOCK_SIZE 0x80
#define PPS_END_OF_CHAIN 0xffffffff

typedef struct _PPS PPS;

#define PPS_SIG 0x13579753
#define IS_PPS(p) (((PPS *)(p))->sig == PPS_SIG)

struct _PPS {
	int      sig;
	char    *name;
	GList   *children;
	PPS     *parent;
	guint32  size;
	BLP      start;
	MsOleType type;
	PPS_IDX  idx; /* Only used on write */
};

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

#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))

#define MAX_CACHED_BLOCKS  32

typedef struct {
	guint32  blk;
	gboolean dirty;
	int      usage;
	guint8   *data;
} BBBlkAttr;

static BBBlkAttr *
bb_blk_attr_new (guint32 blk)
{
	BBBlkAttr *attr = g_new (BBBlkAttr, 1);
	attr->blk   = blk;
	attr->dirty = FALSE;
	attr->usage = 0;
	attr->data  = 0;
	return attr;
}

static void
write_cache_block (MsOle *f, BBBlkAttr *attr)
{
	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 (f->syswrap->lseek (f->file_des, offset, SEEK_SET)==(off_t)-1 ||
	    f->syswrap->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;
}

static guint8 *
get_block_ptr (MsOle *f, BLP b, gboolean forwrite)
{
	BBBlkAttr *attr, *tmp, *min;
	size_t offset;
	guint32 i, blks;

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

	/* Have we cached it ? */
	attr = g_ptr_array_index (f->bbattr, b);
	g_assert (attr);
	g_assert (attr->blk == b);

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

	/* 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 OLE_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);
	
	offset = (b+1)*BB_BLOCK_SIZE;
	f->syswrap->lseek (f->file_des, offset, SEEK_SET);
	f->syswrap->read (f->file_des, attr->data, BB_BLOCK_SIZE);
	attr->usage = 1;
	attr->dirty = forwrite;

	return attr->data;
}


/* 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.
   Each additional list can address 128*512/4*512 blocks ~= 8MBytes */
/* The number of Big Block Descriptor (fat) Blocks */
#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)))
/* The block locations of the Big Block Descriptor Blocks */
#define MAX_SIZE_BBD_LIST           109
/* FIXME tenix next is broken with big files */
#define GET_BBD_LIST(f,i)           (MS_OLE_GET_GUINT32((f)->mem + 0x4c + (i)*4))
/* FIXME tenix next is broken with big files */
#define SET_BBD_LIST(f,i,n)         (MS_OLE_SET_GUINT32((f)->mem + 0x4c + (i)*4, (n)))
#define NEXT_BB(f,n)                (g_array_index ((f)->bb, BLP, n))
#define NEXT_SB(f,n)                (g_array_index ((f)->sb, BLP, n))
/* 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))

/* Get the start block of the root directory ( PPS ) chain */
#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))
/* Get the start block of the SBD chain */
#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))


/* 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. */
#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))
/* These get other interesting stuff from the PPS record */
#define PPS_GET_STARTBLOCK(p)      ( MS_OLE_GET_GUINT32(p + 0x74))
#define PPS_GET_SIZE(p)            ( MS_OLE_GET_GUINT32(p + 0x78))
#define PPS_GET_TYPE(p) ((MsOleType)( MS_OLE_GET_GUINT8(p + 0x42)))
#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))

/* 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)) {                  \
        g_warning ("Tried to mark as unused the block %d which has %d\n",  \
                   (block), g_array_index ((f), BLP, (block)));             \
        } else { g_array_index ((f), BLP, (block)) = UNUSED_BLOCK; } }

/* FIXME: This needs proper unicode support ! current support is a guess */
/* Length is in bytes == 1/2 the final text length */
/* NB. Different from biff_get_text, looks like a bug ! */
static char *
pps_get_text (guint8 *ptr, int length)
{
	int lp;
	char *ans;
	guint16 c;
	guint8 *inb;
	
	length = (length+1)/2;

	if (length <= 0 ||
	    length > (PPS_BLOCK_SIZE/4)) {
#if OLE_DEBUG > 0
		printf ("Nulled name of length %d\n", length);
#endif
		return 0;
	}
	
	ans = (char *) g_malloc (sizeof (char) * length + 1);
	
	inb = ptr;
	for (lp = 0; lp < length; lp++) {
		c = MS_OLE_GET_GUINT16 (inb);
		ans [lp] = (char) c;
		inb += 2;
	}
	ans [lp] = 0;

	return ans;
}

static void
dump_header (MsOle *f)
{
	printf ("--------------------------MsOle HEADER-------------------------\n");
	printf ("Num BBD Blocks : %d Root %%d, SB blocks %d\n",
		f->bb?f->bb->len:-1,
/*		f->pps?f->pps->len:-1, */
/* FIXME tenix, here is not f->num_pps? */
		f->sb?f->sb->len:-1);
	printf ("-------------------------------------------------------------\n");
}

static void
characterise_block (MsOle *f, BLP blk, char **ans)
{
	int nblk;

	nblk = g_array_index (f->bb, BLP, blk);
	if (nblk == UNUSED_BLOCK) {
		*ans = "unused";
		return;
	} else if (nblk == SPECIAL_BLOCK) {
		*ans = "special";
		return;
	} else if (nblk == ADD_BBD_LIST_BLOCK) {
		*ans = "additional special";
		return;
	} else if (nblk == END_OF_CHAIN) {
		*ans = "end of chain";
		return;
	}

	*ans = "unknown";
	g_return_if_fail (f);
	g_return_if_fail (f->bb);
	g_return_if_fail (f->pps);

/*	for (lp=0;lp<f->pps->len;lp++) {
		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;
			}
			cur = NEXT_BB (f, cur);
		}
		}*/
}

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' (size: %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);
	}
}

static void
dump_allocation (MsOle *f)
{
	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);
	}
	
	if (f->pps) {
		printf ("Root blocks : %d\n", f->num_pps); 
		dump_tree (f->pps, 0);
	} else
		printf ("No root yet\n");
/*	
	printf ("sbd blocks : %d\n", h->sbd_list->len);
	for (lp=0;lp<h->sbd_list->len;lp++)
	printf ("sbd_list[%d] = %d\n", lp, (int)ms_array_index (h->sbd_list, SBPtr, lp));*/
	printf ("-------------------------------------------------------------\n");
}

/*
 * Dump some useful facts.
 * magic: 2       : dump tree
 *        default : dump header and allocation
 */
void
ms_ole_debug (MsOle *fs, int magic)
{
	switch (magic) {
	case 2:
		if (fs->pps)
			dump_tree (fs->pps, 0);
		else
			printf ("There are no tree (no pps)\n");
		break;
	default:
		dump_header (fs);
		dump_allocation (fs);
		break;
	}
}

/*
 * get_next_block:
 * @f:   the file handle
 * @blk: an index into the big block fat
 * 
 * Return value: the block index of the BBD block.
 */
static BLP
get_next_block (MsOle *f, BLP blk, gboolean *err)
{
	BLP bbd = GET_BBD_LIST (f, blk / (BB_BLOCK_SIZE / 4));

	if (bbd > (f->length / BB_BLOCK_SIZE)) {
		*err = TRUE;
		return 0;
	} else
		*err = FALSE;
	
	return MS_OLE_GET_GUINT32 (BB_R_PTR (f, bbd) +
				   4 * (blk % (BB_BLOCK_SIZE / 4)));
}

/* Builds the FAT */
static int
read_bb (MsOle *f)
{
	/* FIXME tenix may be later we wish to split this function */
	guint32  numbbd;
	BLP      lp;
	guint32  num_add_bbd_lists;
	BLP      missing_lps;
	BLP      missing_bbds;
	guint32  visited_add_bbd_list;
	BLP tmp;
	BLP bbd;

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

	f->bb   = g_array_new (FALSE, FALSE, sizeof(BLP));
	numbbd  = GET_NUM_BBD_BLOCKS  (f);

        /* Sanity checks */
/* FIXME tenix reading big files
	if (numbbd < ((f->length - BB_BLOCK_SIZE
		      + ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4) - 1)
			 / ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4))) {
		printf ("Duff block descriptors\n");
		return 0;
	}
 */
	/* FIXME tenix check if size is small, there's no add bbd lists */
	
	/* Add BBD's that live in the BBD list */
	for (lp = 0; (lp < (f->length / BB_BLOCK_SIZE) - 1) &&
		     (lp < MAX_SIZE_BBD_LIST * BB_BLOCK_SIZE / 4); lp++) {
		gboolean err;

		tmp = get_next_block (f, lp, &err);
		if (err)
			return 0;

		g_array_append_val (f->bb, tmp);
	}

	/* 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) {
		if (lp != MAX_SIZE_BBD_LIST * BB_BLOCK_SIZE / 4)
			return 0;

		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
		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)))) {
				/* 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)
						+4*MAX_SIZE_ADD_BBD_LIST);
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
			}

			/* 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);
		}
		/* FIXME tenix do we check if we have visited all lp's but
		   there are more additional lists? */
	}

	/* 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);
	}
	if (num_add_bbd_lists > 0) {
		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
		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))) {
				/* 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)
						+ 4*MAX_SIZE_ADD_BBD_LIST);
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
				TRY_MARK_UNUSED_BLOCK (f->bb,
						       visited_add_bbd_list,
						       ADD_BBD_LIST_BLOCK);
			}

			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);
		}
	}

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

	/* More sanity checks */
/* FIXME
	for (lp=0; lp<numbbd; lp++) {
		BLP bbdblk = GET_BBD_LIST(f, lp);
		if (g_array_index(f->bb, BLP, bbdblk) != SPECIAL_BLOCK) {
			printf ("Error - BBD blocks not marked correctly\n");
			g_array_free (f->bb, TRUE);
			return 0;
		}
		}
*/

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

static void
extend_file (MsOle *f, guint blocks)
{
#ifdef HAVE_MMAP
	if (f->ole_mmap) {
		int file;
		guint8 *newptr, zero = 0;
		guint32 filesize;
		guint32 oldlen;
		guint32 icount;
		gchar zeroblock [BB_BLOCK_SIZE];

		memset (zeroblock, zero, BB_BLOCK_SIZE);
		g_assert (f);
		file = f->file_des;
		
		g_assert (munmap(f->mem, f->length) != -1);

		/* Extend that file by blocks */

		if (f->syswrap->getfilesize (file, &filesize)) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}
		if (f->syswrap->lseek (file, 0, SEEK_END) == (off_t)-1) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}
		for (icount = 0; icount < blocks; icount++) {
			if (f->syswrap->write (file, zeroblock, BB_BLOCK_SIZE -
					       ((icount == blocks - 1) ? 1 : 0))
			    == -1) {
				printf ("Serious error extending file\n");
				f->mem = 0;
				return;
			}
		}
		if (f->syswrap->write (file, &zero, 1) == -1) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}

		oldlen = filesize;

		if (f->syswrap->getfilesize (file, &(f->length))) {
			printf ("Warning couldn't get the size of the file\n");
		}
		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);
		/* NOTE tenix here we don't check if try_mmap is true, because
		   if it reach here it means f->ole_mmap is true and try_mmap is
		   true too. This is related with system wrappers. */
		newptr = mmap (f->mem, f->length, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
#if OLE_DEBUG > 0
		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!");
		}
#endif /* OLE_DEBUG */
		f->mem = newptr;
	} else /* !f->ole_mmap */ {
#endif /* HAVE_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;
		}
#ifdef HAVE_MMAP
	}
#endif /* HAVE_MMAP */
}

static BLP
next_free_bb (MsOle *f)
{
	BLP blk, tblk;
  
	g_assert (f);

	blk = 0;
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
	while (blk < f->bb->len)
		if (g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK)
			return blk;
	        else 
			blk++;

	extend_file (f, 1);
	tblk = UNUSED_BLOCK;
	g_array_append_val (f->bb, tblk);
	g_assert ((g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK));
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
	return blk;
}

static int
write_bb (MsOle *f)
{
	guint32 numbbd;
	BLP     lp, lpblk;
	int a = BB_BLOCK_SIZE / 4;

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

	numbbd = (f->bb->len + a - 2) / (a - 1); /* Think really hard! */
	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 */
		guint8 *mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
			     g_array_index (f->bb, BLP, lpblk));
		lpblk++;
	}
	while (lpblk % (BB_BLOCK_SIZE/4) != 0) { /* Undescribed blocks */
		guint8 *mem;
		g_assert (lpblk/(BB_BLOCK_SIZE/4) < numbbd);
		mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
			     UNUSED_BLOCK);
		lpblk++;
	}
	g_array_free (f->bb, TRUE);
	f->bb = 0;
	return 1;
}

static BLP
next_free_sb (MsOle *f)
{
	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);
		if (f->sbf->len > 0)
			g_array_index (f->bb, BLP, 
				       g_array_index (f->sbf, BLP, f->sbf->len-1)) = new_sbf;
		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;
}

static guint8 *
get_pps_ptr (MsOle *f, PPS_IDX i, gboolean forwrite)
{
	int lp;
	BLP blk = GET_ROOT_STARTBLOCK (f);

	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--;
		blk = NEXT_BB (f, blk);
	}
	if (blk == END_OF_CHAIN) {
		printf ("Serious error finding pps %d\n", i);
		return 0;
	}

#if OLE_DEBUG > 0
	printf ("get_pps_ptr: blk = %d\n", blk);
#endif
	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;
}

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);
	
	return g_strcasecmp (b->name, a->name);
}

static void
pps_decode_tree (MsOle *f, PPS_IDX p, PPS *parent)
{
	PPS    *pps;
	guint8 *mem;
       
	if (p == PPS_END_OF_CHAIN)
		return;

	pps           = g_new (PPS, 1);
	pps->sig      = PPS_SIG;
	mem           = get_pps_ptr (f, p, FALSE);
	if (!mem) {
		printf ("Serious directory error %d\n", p);
		f->pps = NULL;
		return;
	}
#if OLE_DEBUG > 0
	printf ("pps_decode_tree: mem (offset)= %#8.8x\n", mem - f->mem);
#endif
	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;
	pps->idx      = 0;
	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);
	}

	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);
	
#if OLE_DEBUG > 1
	printf ("PPS decode : '%s'\n", pps->name?pps->name:"Null");
	ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif
	return;
}

static int
read_pps (MsOle *f)
{
	PPS *pps;
	g_return_val_if_fail (f, 0);

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

	if (!f->pps || g_list_length (f->pps) < 1 ||
	    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;
	}

	/* 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");

	{ /* Free up the root chain */
		BLP blk, last;
		last = blk = GET_ROOT_STARTBLOCK (f);
		while (blk != END_OF_CHAIN) {
			last = blk;
			blk = NEXT_BB (f, blk);
			g_array_index (f->bb, BLP, last) = UNUSED_BLOCK;
		}
	}
	
	if (!f->pps) {
		printf ("Root directory too small\n");
		return 0;
	}
	return 1;
}

/**
 * Write the blocks main data recursively.
 **/
static void
pps_encode_tree_initial (MsOle *f, GList *list, PPS_IDX *p)
{
	int lp, max;
	guint8 *mem;
	PPS    *pps;

	g_return_if_fail (list);
	g_return_if_fail (list->data);
	
	pps = list->data;
	pps->idx = *p;
	(*p)++;

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

	mem = get_pps_ptr (f, pps->idx, TRUE);

	/* Blank stuff I don't understand */
	for (lp=0;lp<PPS_BLOCK_SIZE;lp++)
		MS_OLE_SET_GUINT8(mem+lp, 0);
	if (pps->name) {
		max = strlen (pps->name);
		if (max >= (PPS_BLOCK_SIZE/4))
			max = (PPS_BLOCK_SIZE/4);
		for (lp=0;lp<max;lp++)
			MS_OLE_SET_GUINT16(mem + lp*2, pps->name[lp]);
	} else {
		printf ("No name %d\n", *p);
		max = -1;
	}
	PPS_SET_NAME_LEN(mem, (max+1)*2);
	
	/* Magic numbers */
	if (pps->idx == PPS_ROOT_INDEX) { /* Only Root */
		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 ? */
	} else if (pps->size >= BB_THRESHOLD) {
		MS_OLE_SET_GUINT32  (mem + 0x50, 0x00020900);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x01);
	} else {
		MS_OLE_SET_GUINT32  (mem + 0x64, 0x09299c3c);
		MS_OLE_SET_GUINT32  (mem + 0x6c, 0x09299c3c);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x00);
	}

	PPS_SET_TYPE (mem, pps->type);
	PPS_SET_SIZE (mem, pps->size);
        PPS_SET_STARTBLOCK(mem, pps->start);
	PPS_SET_NEXT (mem, PPS_END_OF_CHAIN);
	PPS_SET_PREV (mem, PPS_END_OF_CHAIN);
	PPS_SET_DIR  (mem, PPS_END_OF_CHAIN);

#if OLE_DEBUG > 1
	printf ("Encode '%s' as \n", pps->name);
	ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif

	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
 */
static void
pps_encode_tree_chain (MsOle *f, GList *list)
{
	PPS	*parent;		/* parent's PPS */
	int	len;			/* how many childrens are there */
	GList	*lchildren;		/* visited children */
        PPS	*children;		/* visited children's PPS */
	PPS     *next;			/* next children's PPS */
	PPS	*prev;			/* previous children's PPS */
	guint8	*mem;			/* a PPS in memory */
	guint8	*mem_parent;		/* a PPS in memory */
	gint	i;
	int	half_way;

	g_return_if_fail (list);
	g_return_if_fail (list->data);

	parent = list->data;
	len = g_list_length (parent->children);
	half_way = len / 2;
	lchildren = parent->children;

	/* The base node of the directory */

	/* Choose the first child */
	mem_parent = get_pps_ptr (f, parent->idx, TRUE);

	if (len == 1) {
		PPS_SET_DIR (mem_parent, ((PPS *)(lchildren->data))->idx);

#if OLE_DEBUG > 1
		printf ("tenix3 Final encode '%s' as \n",
			((PPS *)(parent))->name);
		ms_ole_dump (mem_parent, PPS_BLOCK_SIZE);
		printf ("tenix3 Final encode '%s' as \n",
			((PPS *)(lchildren->data))->name);
		ms_ole_dump (get_pps_ptr (f, ((PPS *)(lchildren->data))->idx, FALSE),
		      PPS_BLOCK_SIZE);
#endif

		return;
	}

#if OLE_DEBUG > 1
	if (len == 0)
		printf ("Empty directory '%s'\n", ((PPS *)(children))->name);
#endif

	i = 0;
	for (; lchildren; lchildren = g_list_next (lchildren)) {
		children = lchildren->data;

		if (children->type == MsOleStorageT)
			pps_encode_tree_chain (f, lchildren);

		if (i == half_way)
			PPS_SET_DIR (mem_parent, ((PPS *)(children))->idx);

		mem = get_pps_ptr (f, children->idx, TRUE);
		if (i == half_way) {
			if (g_list_previous (lchildren)) {
				prev = g_list_previous(lchildren)->data;
				PPS_SET_PREV (mem, prev->idx);
			}

			if (g_list_next (lchildren)) {
				next = g_list_next (lchildren)->data;
				PPS_SET_NEXT (mem, next->idx);
			}
		} else if (i < half_way) {
			if (g_list_previous(lchildren)) {
				prev = g_list_previous (lchildren)->data;
				PPS_SET_PREV (mem, prev->idx);
			}
		} else /* i > half_way */ {
			if (g_list_next(lchildren)) {
				next = g_list_next (lchildren)->data;
				PPS_SET_NEXT (mem, next->idx);
			}
		}

#if OLE_DEBUG > 1
			printf ("tenix1 Final encode '%s' as \n",
				((PPS *)(children))->name);
			ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif

		i++;
	}

#if OLE_DEBUG > 1
	printf ("tenix2 Final encode '%s' as \n", ((PPS *)(parent))->name);
	ms_ole_dump (mem_parent, PPS_BLOCK_SIZE);
#endif
}


static int
write_pps (MsOle *f)
{
	int lp;
	PPS_IDX idx;
	BLP blk  = END_OF_CHAIN;
	BLP last = END_OF_CHAIN;

	/* 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 {
#if OLE_DEBUG > 0
			printf ("Set root block to %d\n", blk);
#endif
			SET_ROOT_STARTBLOCK (f, blk);
		}
		g_array_index (f->bb, BLP, blk) = END_OF_CHAIN;
	}

	g_assert (GET_ROOT_STARTBLOCK(f) != END_OF_CHAIN);

	idx    = PPS_ROOT_INDEX;
	pps_encode_tree_initial (f, f->pps, &idx);
	pps_encode_tree_chain   (f, f->pps);

	f->pps = 0;
	f->num_pps = 0;
	return 1;
}

static int
read_sb (MsOle *f)
{
	BLP ptr;
	int lastidx, idx;
	PPS *root;

	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->pps, 0);

	root = f->pps->data;
	g_return_val_if_fail (root, 0);

	f->sbf = g_array_new (FALSE, FALSE, sizeof(BLP));
	f->sb  = g_array_new (FALSE, FALSE, sizeof(BLP));
	
	/* List of big blocks in SB file */
	ptr = root->start;
#if OLE_DEBUG > 0
	printf ("Starting Small block file at %d\n", root->start);
#endif
	while (ptr != END_OF_CHAIN) {
		if (ptr == UNUSED_BLOCK ||
		    ptr == SPECIAL_BLOCK) {
			printf ("Corrupt small block file: serious error, "
				"invalid block in chain\n");
			g_array_free (f->sbf, TRUE);
			f->sbf = 0;
			return 0;
		}
		g_array_append_val (f->sbf, ptr);
		ptr = NEXT_BB (f, ptr);
	}

	/* Description of small blocks */
	lastidx = -1;
	idx     = 0;
	ptr = GET_SBD_STARTBLOCK (f);

	if (f->sbf->len == 0 && ptr != END_OF_CHAIN) {
		printf ("No small block file, but small block depot start block exists!: "
			"ignore depot, since there's no small block files after all.\n");
		ptr = END_OF_CHAIN;
	}

	while (ptr != END_OF_CHAIN) {
		guint32 lp;
		if (ptr == UNUSED_BLOCK ||
		    ptr == SPECIAL_BLOCK) {
			printf ("Corrupt file descriptor: serious error, "
				"invalid block in chain\n");
			g_array_free (f->sb, TRUE);
			f->sb = 0;
			return 0;
		}
		for (lp=0;lp<BB_BLOCK_SIZE/4;lp++) {
			BLP p = MS_OLE_GET_GUINT32 (BB_R_PTR(f, ptr) + lp*4);
			g_array_append_val (f->sb, p);
			
			if (p != UNUSED_BLOCK)
				lastidx = idx;
			idx++;
		}
		ptr = NEXT_BB (f, ptr);
	}
	if (lastidx>0)
		g_array_set_size (f->sb, lastidx+1);
	
	if (f->sbf->len * BB_BLOCK_SIZE < f->sb->len*SB_BLOCK_SIZE) {
		printf ("Not enough small block file for descriptors\n"
			"sbf->len == %d, sb->len == %d\n", f->sbf->len,
			f->sb->len);
		return 0;
	}

	return 1;
}

static int
write_sb (MsOle *f)
{
	guint32 lp, lastused;
	PPS *root;
	BLP sbd_start  = END_OF_CHAIN;
	BLP sbf_start  = END_OF_CHAIN;

	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->pps, 0);

	root = f->pps->data;

	if (f->sbf->len * BB_BLOCK_SIZE < f->sb->len*SB_BLOCK_SIZE) {
		printf ("Not enough descriptor / blocks being written %d %d\n",
			f->sbf->len, f->sb->len);
	}
	if (f->sbf->len>0)
		sbf_start = g_array_index (f->sbf, BLP, 0);

	lastused = END_OF_CHAIN;
	for (lp=0;lp<f->sb->len;lp++) {
		if (g_array_index (f->sb, BLP, lp) != UNUSED_BLOCK)
			lastused = lp;
	}

	if (lastused != END_OF_CHAIN) { /* Bother writing stuff */
		guint8 *mem = 0;
		guint32 num_sbdf = (lastused + (BB_BLOCK_SIZE/4)-1) /
			(BB_BLOCK_SIZE/4);
		BLP blk = END_OF_CHAIN, last;

#if OLE_DEBUG > 0
		printf ("Num SB descriptor blocks : %d\n", num_sbdf);
#endif

		for (lp=0;lp<num_sbdf*(BB_BLOCK_SIZE/4);lp++) {
			BLP set;
			if (lp%(BB_BLOCK_SIZE/4) == 0) {
				last = blk;
				blk = next_free_bb(f);
				if (!lp)
					sbd_start = blk;
				if (last != END_OF_CHAIN)
					g_array_index (f->bb, BLP, last) = blk;
				g_array_index (f->bb, BLP, blk) = END_OF_CHAIN;
				mem = BB_W_PTR (f, blk);
			}
			if (lp<f->sb->len)
				set = g_array_index (f->sb, BLP, lp);
			else
				set = UNUSED_BLOCK;
			MS_OLE_SET_GUINT32 (mem + (lp%(BB_BLOCK_SIZE/4))*4, set);
		}
	} else {
#if OLE_DEBUG > 0
		printf ("Blank SB allocation\n");
#endif
		sbf_start = END_OF_CHAIN;
	}

	root->start = sbf_start;
	SET_SBD_STARTBLOCK (f, sbd_start);
	g_array_free (f->sb,  TRUE);
	g_array_free (f->sbf, TRUE);
	f->sb       = 0;
	f->sbf      = 0;
	return 1;
}

static int
ms_ole_setup (MsOle *f)
{
	if (!f->ole_mmap) {
		guint32 i;
		f->bbattr = g_ptr_array_new ();
		for (i = 0; i <(f->length / BB_BLOCK_SIZE) + 1; i++)
			g_ptr_array_add (f->bbattr, bb_blk_attr_new(i));
	}
	
	if (read_bb  (f) &&
	    read_pps (f) &&
	    read_sb  (f)) {
#if OLE_DEBUG > 1
		printf ("Just read header of\n");
		dump_header (f);
#endif		
		return 1;
	}
	return 0;
}

static int
ms_ole_cleanup (MsOle *f)
{
	if (f->mode != 'w') /* Nothing to write */
		return 1;
#if OLE_DEBUG > 1
	printf ("About to write header of: \n");
	dump_header (f);
#endif
	if (write_sb  (f) &&
	    write_pps (f) &&
	    write_bb  (f))
		return 1;
	return 0;
}

static MsOle *
new_null_msole ()
{
	MsOle *f = g_new0 (MsOle, 1);

	f->mem    = (guint8 *)0xdeadbeef;
	f->length = 0;
	f->mode   = 'r';
	f->bb     = 0;
	f->bbattr = 0;
	f->sb     = 0;
	f->sbf    = 0;
	f->pps    = 0;
	f->dirty  = 0;

	return f;
}


/**
 * ms_ole_ref:
 * @fs: filesystem object.
 * 
 * Increment by one the count of references to the filesystem.
 **/
void
ms_ole_ref (MsOle *fs)
{
	g_return_if_fail (fs != NULL);
	fs->ref_count++;
}


/**
 * ms_ole_unref:
 * @fs: filesystem object.
 * 
 * Decrement by one the count of references to the filesystem.
 **/
void
ms_ole_unref (MsOle *fs)
{
	g_return_if_fail (fs != NULL);
	fs->ref_count--;
}


/**
 * ms_ole_open_vfs:
 * @fs: filesystem object.
 * @path: path to the filesystem-in-the file on the actual filesystem.
 * @try_mmap: TRUE if try to mmap(2) the filesystem-in-a-file,
 *            instead of opening.
 * @wrappers: system functions wrappers, %NULL if standard functions are used.
 * 
 * Opens the filesystem-in-the-file @path and creates the filesystem object @fs.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_open_vfs (MsOle **f, const char *name, gboolean try_mmap,
		 MsOleSysWrappers *wrappers)
{
#ifdef HAVE_MMAP
	int prot = PROT_READ | PROT_WRITE;
#endif
	int file;

	if (!f)
		return MS_OLE_ERR_BADARG;

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif

	*f = new_null_msole();
	take_wrapper_functions (*f, wrappers);
	(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDWR);
	(*f)->ref_count = 0;
	(*f)->mode = 'w';
	if (file == -1) {
		(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDONLY);
		(*f)->mode = 'r';
		prot &= ~PROT_WRITE;
	}
	if ((file == -1) || !((*f)->syswrap->isregfile (file))) {
		/* FIXME tenix is not a memory leak not to close file? */
		printf ("No such file '%s'\n", name);
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->syswrap->getfilesize( file, &((*f)->length) )) {
		printf ("Couldn't get the size of file '%s'\n", name);
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->length <= 0x4c) { /* Bad show */
#if OLE_DEBUG > 0
		printf ("File '%s' too short\n", name);
#endif
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_FORMAT;
	}

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, prot, MAP_SHARED, file, 0);

		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

			if (!(*f)->mem
			    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
				== -1)) {
				printf ("Error reading header\n");
				g_free (*f);
				return MS_OLE_ERR_EXIST;
			}
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

		if (!(*f)->mem
		    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
			== -1)) {
			printf ("Error reading header\n");
			g_free (*f);
			return MS_OLE_ERR_EXIST;
		}
	} /* try_mmap */
	
	if (MS_OLE_GET_GUINT32((*f)->mem    ) != 0xe011cfd0 ||
	    MS_OLE_GET_GUINT32((*f)->mem + 4) != 0xe11ab1a1) {
#if OLE_DEBUG > 0
		printf ("Failed OLE2 magic number %x %x\n",
			MS_OLE_GET_GUINT32((*f)->mem), MS_OLE_GET_GUINT32((*f)->mem+4));
#endif
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file '%s':%d non-integer number of blocks\n",
			name, (*f)->length);

	if (!ms_ole_setup (*f)) {
		printf ("'%s' : duff file !\n", name);
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}

	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif
	/* If writing then when destroy commit it */
	return MS_OLE_ERR_OK;
}


MsOleErr ms_ole_open_vfs_body (MsOle **f, const char *name, gboolean try_mmap, MsOleSysWrappers *wrappers, char *jp_buf_ptr, size_t jp_buf_len)
{
#ifdef HAVE_MMAP
	int prot = PROT_READ | PROT_WRITE;
#endif
	int file;

	if (!f)
		return MS_OLE_ERR_BADARG;

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif

	*f = new_null_msole();

	jp_msOle = *f;

	(*f)->buf = jp_buf_ptr;
	(*f)->len = jp_buf_len;
	(*f)->idx = 0;
		
	// take_wrapper_functions (*f, wrappers);
	take_wrapper_functions (*f, &default_wrappers_body);
		
	(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDWR);
	(*f)->ref_count = 0;
	(*f)->mode = 'w';
	
	if (file == -1) {
		(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDONLY);
		(*f)->mode = 'r';
		prot &= ~PROT_WRITE;
	}
	if ((file == -1) || !((*f)->syswrap->isregfile (file))) {
		/* FIXME tenix is not a memory leak not to close file? */
		printf ("No such file '%s'\n", name);
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->syswrap->getfilesize( file, &((*f)->length) )) {
		printf ("Couldn't get the size of file '%s'\n", name);
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->length <= 0x4c) { /* Bad show */
#if OLE_DEBUG > 0
		printf ("File '%s' too short\n", name);
#endif
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_FORMAT;
	}

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, prot, MAP_SHARED, file, 0);

		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

			if (!(*f)->mem
			    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
				== -1)) {
				printf ("Error reading header\n");
				g_free (*f);
				return MS_OLE_ERR_EXIST;
			}
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

		if (!(*f)->mem
		    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
			== -1)) {
			printf ("Error reading header\n");
			g_free (*f);
			return MS_OLE_ERR_EXIST;
		}
	} /* try_mmap */
	
	if (MS_OLE_GET_GUINT32((*f)->mem    ) != 0xe011cfd0 ||
	    MS_OLE_GET_GUINT32((*f)->mem + 4) != 0xe11ab1a1) {
#if OLE_DEBUG > 0
		printf ("Failed OLE2 magic number %x %x\n",
			MS_OLE_GET_GUINT32((*f)->mem), MS_OLE_GET_GUINT32((*f)->mem+4));
#endif
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}
	
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file '%s':%d non-integer number of blocks\n", name, (*f)->length);

	if (!ms_ole_setup (*f)) {
		printf ("'%s' : duff file !\n", name);
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}

	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif
	/* If writing then when destroy commit it */
	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_create_vfs:
 * @fs: filesystem object.
 * @path: path to the filesystem-in-the file on the actual filesystem.
 * @try_mmap: TRUE if try to mmap(2) the filesystem-in-a-file,
 *            instead of opening.
 * @wrappers: system functions wrappers, %NULL if standard functions are used.
 * 
 * Creates the filesystem-in-the-file @path and creates the filesystem @fs.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_create_vfs (MsOle **f, const char *name, gboolean try_mmap,
		   MsOleSysWrappers *wrappers)
{
	int file, zero=0;
	int init_blocks = 1, lp;

	if (!f)
		return MS_OLE_ERR_BADARG;

	*f = new_null_msole ();
	take_wrapper_functions (*f, wrappers);
	if ((file = (*f)->syswrap->open3 (name,
					  O_RDWR|O_CREAT|O_TRUNC|O_NONBLOCK,
					  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP))
	    == -1) {
		printf ("Can't create file '%s'\n", name);
		g_free (*f);
		*f = NULL;
		return MS_OLE_ERR_PERM;
	}

	if (((*f)->syswrap->lseek (file, BB_BLOCK_SIZE * init_blocks - 1,
		    SEEK_SET) == (off_t)-1) ||
	    ((*f)->syswrap->write (file, &zero, 1) == -1)) {
		printf ("Serious error extending file to %d bytes\n",
			BB_BLOCK_SIZE*init_blocks);
		g_free (*f);
		*f = NULL;
		return MS_OLE_ERR_SPACE;
	}

	(*f)->ref_count = 0;
	(*f)->file_des  = file;
	(*f)->mode      = 'w';
	if ((*f)->syswrap->getfilesize (file, &((*f)->length))) {
		printf ("Warning couldn't get the size of the file '%s'\n",
			name);
	}
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file %d non-integer number of blocks\n",
			(*f)->length);

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, PROT_READ|PROT_WRITE,
				  MAP_SHARED, file, 0);
		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem  = g_new (guint8, BB_BLOCK_SIZE);
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem  = g_new (guint8, BB_BLOCK_SIZE);
	} /* try_mmap */

	/* The header block */
	for (lp = 0; lp < BB_BLOCK_SIZE / 4; lp++)
		MS_OLE_SET_GUINT32((*f)->mem + lp * 4,
				   (lp < (0x52 / 4)) ? 0: UNUSED_BLOCK);

	MS_OLE_SET_GUINT32((*f)->mem,     0xe011cfd0); /* Magic number */
	MS_OLE_SET_GUINT32((*f)->mem + 4, 0xe11ab1a1);

	/* More magic numbers */
	MS_OLE_SET_GUINT32((*f)->mem + 0x18, 0x0003003e);
	MS_OLE_SET_GUINT32((*f)->mem + 0x1c, 0x0009fffe);
	MS_OLE_SET_GUINT32((*f)->mem + 0x20, 0x6); 
	MS_OLE_SET_GUINT32((*f)->mem + 0x38, 0x00001000); 
/*	MS_OLE_SET_GUINT32((*f)->mem + 0x40, 0x1);  */
	MS_OLE_SET_GUINT32((*f)->mem + 0x44, 0xfffffffe); 

	SET_NUM_BBD_BLOCKS  (*f, 0);
	SET_ROOT_STARTBLOCK (*f, END_OF_CHAIN);
	SET_SBD_STARTBLOCK  (*f, END_OF_CHAIN);

	{
		PPS *p;

		(*f)->bb  = g_array_new (FALSE, FALSE, sizeof(BLP));
		(*f)->sb  = g_array_new (FALSE, FALSE, sizeof(BLP));
		(*f)->sbf = g_array_new (FALSE, FALSE, sizeof(BLP));
		p           = g_new(PPS, 1);
		p->sig      = PPS_SIG;
		p->name     = g_strdup ("Root Entry");
		p->start    = END_OF_CHAIN;
		p->type     = MsOleRootT;
		p->size     = 0;
		p->children = NULL;
		p->parent   = NULL;
		(*f)->pps = g_list_append (0, p);
		(*f)->num_pps = 1;

		if ((*f)->ole_mmap)
			(*f)->bbattr = NULL;
		else
			(*f)->bbattr   = g_ptr_array_new ();
	}
	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

	return MS_OLE_ERR_OK;
}


static void
destroy_pps (GList *l)
{
	GList *tmp;

	for (tmp = l; tmp; tmp = g_list_next (tmp)) {
		PPS *pps = tmp->data;
		if (pps->name)
			g_free (pps->name);
		destroy_pps (pps->children);
		g_free (pps);
	}
	g_list_free (l);
}


/**
 * ms_ole_destroy:
 * @fs: filesystem object.
 * 
 * Closes the filesystem @fs and truncates any free blocks.
 **/
void
ms_ole_destroy (MsOle **ptr)
{
	MsOle *f = *ptr;

#if OLE_DEBUG > 0
	printf ("FIXME: should truncate to remove unused blocks\n");
#endif
	if (f) {
		if (f->ref_count != 0)
			g_warning ("Unclosed files exist on this OLE stream");

		if (f->dirty)
			ms_ole_cleanup (f);

		if (f->mem == (void *)0xdeadbeef)
		    f->mem = NULL;
		else if (f->ole_mmap) {
#ifdef HAVE_MMAP
			munmap (f->mem, f->length);
#else
			g_warning ("Unmapping while we dont have mmap call");
#endif
		} else {
			guint32 i;
			for (i = 0; (f->bbattr) && (i < f->bbattr->len); i++) {
				BBBlkAttr *attr = g_ptr_array_index (f->bbattr, i);
				if (f->dirty && attr->dirty)
					write_cache_block (f, attr);
				g_free (attr->data);
				attr->data = 0;
			}
			if (f->dirty) {
				f->syswrap->lseek (f->file_des, 0, SEEK_SET);
				f->syswrap->write (f->file_des, f->mem,
						   BB_BLOCK_SIZE);
			}
			g_free (f->mem);
			f->mem = 0;
		}

		destroy_pps (f->pps);

		f->syswrap->close (f->file_des);
		g_free (f);

#if OLE_DEBUG > 0
		printf ("Closing OLE file\n");
#endif
	}
	*ptr = NULL;
}


/**
 * ms_ole_dump:
 * @ptr: memory area to be dumped.
 * @len: how many bytes will be dumped.
 * 
 * Dump @len bytes from the memory location given by @ptr.
 **/
void
ms_ole_dump (guint8 const *ptr, guint32 len)
{
	guint32 lp,lp2;
	guint32 off;

	for (lp = 0;lp<(len+15)/16;lp++)
	{
		printf ("%8x | ", lp*16);
		for (lp2=0;lp2<16;lp2++) {
			off = lp2 + (lp<<4);
			off<len?printf("%2x ", ptr[off]):printf("XX ");
		}
		printf ("| ");
		for (lp2=0;lp2<16;lp2++) {
			off = lp2 + (lp<<4);
			printf ("%c", off<len?(ptr[off]>'!'&&ptr[off]<127?ptr[off]:'.'):'*');
		}
		printf ("\n");
	}
}


/*
 * Redundant stream check function.
 */
static void
check_stream (MsOleStream *s)
{
	BLP blk;
	guint32 idx;
	PPS *p;
	MsOle *f;

	g_return_if_fail (s);
	g_return_if_fail (s->file);

	f = s->file;
	p = s->pps;

	g_return_if_fail (p);
	blk = p->start;
	idx = 0;
	if (s->type == MsOleSmallBlock) {
		while (blk != END_OF_CHAIN) {
			g_assert (g_array_index (s->blocks, BLP, idx) ==
				  blk);
#if OLE_DEBUG > 2
			ms_ole_dump (GET_SB_R_PTR(f, blk), SB_BLOCK_SIZE);
#endif
			blk = NEXT_SB (f, blk);
			idx++;
		}
	} else {
		while (blk != END_OF_CHAIN) {
			g_assert (g_array_index (s->blocks, BLP, idx) ==
				  blk);
#if OLE_DEBUG > 2
			ms_ole_dump (BB_R_PTR(f, blk), BB_BLOCK_SIZE);
#endif
			blk = NEXT_BB (f, blk);
			idx++;
		}
	}
}


static MsOlePos
tell_pos (MsOleStream *s)
{
	return s->position;
}


/*
 * Free the allocation chains, and free up the blocks.
 * "It was for freedom that Christ has set us free."
 *   Galatians 5:11
 */
static void
free_allocation (MsOle *f, guint32 startblock, gboolean is_big_block_stream)
{
	g_return_if_fail (f);

#if OLE_DEBUG > 0
	printf ("Free allocation %d : (%d)\n", startblock,
		is_big_block_stream);
#endif
       
	if (is_big_block_stream)
	{
		BLP p = startblock;
		printf ("FIXME: this should also free up blocks\n");
		while (p != END_OF_CHAIN) {
			BLP next = NEXT_BB (f,p);
			if (next == p) {
				printf ("Serious bug: cyclic ring in BB allocation\n");
				return;
			} else if (p == SPECIAL_BLOCK ||
				   p == UNUSED_BLOCK) {
				printf ("Serious bug: Special / Unused block "
					"in BB allocation\n");
				return;
			}
			g_array_index (f->bb, BLP, p) = UNUSED_BLOCK;
			p = next;
		}
	}
	else
	{
		BLP p = startblock;
		while (p != END_OF_CHAIN) {
			BLP next = NEXT_SB (f,p);
			if (next == p) {
				printf ("Serious bug: cyclic ring in SB allocation\n");
				return;
			} else if (p == SPECIAL_BLOCK ||
				   p == UNUSED_BLOCK) {
				printf ("Serious bug: Special / Unused block "
					"in SB allocation\n");
				return;
			}
			g_array_index (f->sb, BLP, p) = UNUSED_BLOCK;
			p = next;
		}
		/* Seek forwards to find blank sbf blocks */
		{
			guint32 lp;
			BLP     lastused = END_OF_CHAIN;
			for (lp=0;lp<f->sb->len;lp++) {
				if (g_array_index (f->sb, BLP, lp) != UNUSED_BLOCK)
					lastused = lp;
			}
			if (lastused == END_OF_CHAIN) {
				for (lp=0;lp<f->sbf->len;lp++) {
					BLP sbfd = g_array_index (f->sbf, BLP, lp);
					g_array_index (f->bb, BLP, sbfd) = UNUSED_BLOCK;
				}
				g_array_set_size (f->sbf, 0);
				g_array_set_size (f->sb, 0);
			} else {
				guint32 sbf_needed = (lastused+(BB_BLOCK_SIZE/SB_BLOCK_SIZE)-1) /
					             (BB_BLOCK_SIZE/SB_BLOCK_SIZE);

				if (sbf_needed == f->sbf->len)
					return;
				
				for (lp=sbf_needed;lp<f->sbf->len;lp++) {
					BLP sbfd = g_array_index (f->sbf, BLP, lp);
					g_array_index (f->bb, BLP, sbfd) = UNUSED_BLOCK;
				}
				g_array_set_size (f->sbf, sbf_needed);
				g_array_set_size (f->sb, lastused+1);
			}
		}
	}
}


/**
 * ms_ole_lseek:
 * @s: stream object.
 * @bytes: number of bytes to set the stream pointer.
 * @type: relative from where the stream pointer will be set.
 * 
 * Set the stream pointer for @s as many as @bytes bytes according to @type.
 * 
 * Return value: the new position of the stream pointer.
 **/
static MsOleSPos
ms_ole_lseek (MsOleStream *s, MsOleSPos bytes, MsOleSeek type)
{
	/* FIXME tenix improve limits detection: avoid gint vs guint limits */
	MsOleSPos newpos;

	g_return_val_if_fail (s, -1);

	newpos = s->position;
	if (type == MsOleSeekSet)
		newpos  = bytes;
	else if (type == MsOleSeekCur)
		newpos += bytes;
	else
		newpos = s->size - bytes;

	if (newpos > s->size || newpos < 0) {
		g_warning ("Invalid seek");
		return -1;
	}
	s->position = newpos;
	return newpos;
}


/*
 *  Returns:
 *  NULL    - on error
 */
static guint8*
ms_ole_read_ptr_bb (MsOleStream *s, MsOlePos length)
{
	int blklen;
	guint8 *ans;
	guint32 len=length;
	int blockidx = s->position/BB_BLOCK_SIZE;

	g_return_val_if_fail (s, NULL);

	if (!s->blocks || blockidx >= s->blocks->len) {
		printf ("Reading from NULL file\n");
		return NULL;
	}

	blklen = BB_BLOCK_SIZE - s->position%BB_BLOCK_SIZE;

	if (len > blklen && !s->file->ole_mmap)
		return NULL;

	while (len > blklen) {
		len -= blklen;
		blklen = BB_BLOCK_SIZE;
		if (blockidx >= (s->blocks->len - 1)
		    || (ms_array_index (s->blocks, BLP, blockidx)
			!= blockidx + 1))
			return NULL;
		blockidx++;
	}
	/* Straight map, simply return a pointer */
	ans = BB_R_PTR (s->file, ms_array_index (s->blocks, BLP,
						 s->position/BB_BLOCK_SIZE))
		+ s->position%BB_BLOCK_SIZE;
	ms_ole_lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return ans;
}


/*
 *  Returns:
 *  NULL    - on error
 */
static guint8*
ms_ole_read_ptr_sb (MsOleStream *s, MsOlePos length)
{
	int blklen;
	guint8 *ans;
	guint32 len=length;
	int blockidx = s->position/SB_BLOCK_SIZE;

	g_return_val_if_fail (s, NULL);

	if (!s->blocks || blockidx >= s->blocks->len) {
		printf ("Reading from NULL file\n");
		return NULL;
	}

	blklen = SB_BLOCK_SIZE - s->position%SB_BLOCK_SIZE;

	if (len > blklen && !s->file->ole_mmap)
		return NULL;

	while (len > blklen) {
		len -= blklen;
		blklen = SB_BLOCK_SIZE;
		if (blockidx >= (s->blocks->len - 1)
		    || (ms_array_index (s->blocks, BLP, blockidx)
			!= blockidx + 1))
			return NULL;
		blockidx++;
	}
	/* Straight map, simply return a pointer */
	ans = GET_SB_R_PTR (s->file, ms_array_index (s->blocks, BLP,
						     s->position/SB_BLOCK_SIZE))
		+ s->position%SB_BLOCK_SIZE;
	ms_ole_lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return ans;
}


/*
 *  Returns:
 *  zero    - on error
 *  no zero - on success
 */
static gint
ms_ole_read_copy_bb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8 *src;
	int offset = s->position % BB_BLOCK_SIZE;
	int blkidx = s->position / BB_BLOCK_SIZE;

	g_return_val_if_fail (s, 0);
	g_return_val_if_fail (ptr, 0);

	
	if (!s->blocks) {
		printf ("Reading from NULL file\n");
		return 0;
	}

	while (length > 0)
	{
		BLP block;
		int cpylen = BB_BLOCK_SIZE - offset;
		if (cpylen > length)
			cpylen = length;

		if (s->position + cpylen > s->size
		    || blkidx == s->blocks->len) {
#if OLE_DEBUG > 0
			printf ("Trying 2 to read beyond end of stream %d+%d %d\n",
				s->position, cpylen, s->size);
#endif
			return 0;
		}
		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		src = BB_R_PTR (s->file, block) + offset;
		
		memcpy (ptr, src, cpylen);
		ptr    += cpylen;
		length -= cpylen;
		
		offset = 0;
		
		blkidx++;
		s->position+=cpylen;
	}

	if (libole2_debug)
		check_stream (s);

	return 1;
}


/*
 *  Returns:
 *  zero    - on error
 *  no zero - on success
 */
static gint
ms_ole_read_copy_sb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	int offset = s->position%SB_BLOCK_SIZE;
	int blkidx = s->position/SB_BLOCK_SIZE;
	guint8 *src;

	g_return_val_if_fail (s, 0);
	g_return_val_if_fail (ptr, 0);

	
	if (!s->blocks) {
		printf ("Reading from NULL file\n");
		return 0;
	}

	while (length > 0)
	{
		int cpylen = SB_BLOCK_SIZE - offset;
		BLP block;
		if (cpylen>length)
			cpylen = length;
		if (s->position + cpylen > s->size
		    || blkidx == s->blocks->len) {
#if OLE_DEBUG > 0
			printf ("Trying 3 to read beyond end of stream %d+%d %d\n",
				s->position, cpylen, s->size);
#endif
			return 0;
		}
		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		src = GET_SB_R_PTR(s->file, block) + offset;
		
		memcpy (ptr, src, cpylen);
		ptr += cpylen;
		length -= cpylen;
		
		offset = 0;

		blkidx++;
		s->position+=cpylen;
	}

	if (libole2_debug)
		check_stream (s);

	return 1;
}


static void
ms_ole_append_block (MsOleStream *s)
{
	BLP block;
	BLP lastblk = END_OF_CHAIN;
	BLP eoc     = END_OF_CHAIN;

	if (s->type==MsOleSmallBlock) {
		if (!s->blocks)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));

		else if (s->blocks->len>0)
			lastblk = ms_array_index (s->blocks, BLP, s->blocks->len-1);

		block = next_free_sb (s->file);
		g_array_append_val (s->blocks, block);

		if (lastblk != END_OF_CHAIN) { /* Link onwards */
			g_array_index (s->file->sb, BLP, lastblk) = block;
#if OLE_DEBUG > 1
			printf ("Chained Small block %d to previous block %d\n", block, lastblk);
#endif
		} else { /* First block in a file */
			PPS *p = s->pps;
#if OLE_DEBUG > 0
			printf ("Set first Small block to %d\n", block);
#endif
			p->start = block;
		}

		g_array_index (s->file->sb, BLP, block) = eoc;
	} else {
		if (!s->blocks)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));
		else if (s->blocks->len>0)
			lastblk = ms_array_index (s->blocks, BLP, s->blocks->len-1);

		block = next_free_bb (s->file);
#if OLE_DEBUG > 0
		{
			int lp;
			g_assert (g_array_index (s->file->bb, BLP, block) == UNUSED_BLOCK);
			for (lp=0;lp<s->blocks->len;lp++)
				g_assert (g_array_index (s->blocks, BLP, lp) != block);
		}
#endif
		g_array_append_val (s->blocks, block);

		if (lastblk != END_OF_CHAIN) { /* Link onwards */
			g_array_index (s->file->bb, BLP, lastblk) = block;
#if OLE_DEBUG > 1
			printf ("Chained Big block %d to block %d\n", block, lastblk);
#endif
		} else { /* First block in a file */
			PPS *p = s->pps;
#if OLE_DEBUG > 0
			printf ("Set first Big block to %d\n", block);
#endif
			p->start = block;
		}

		g_array_index (s->file->bb, BLP, block) = eoc;
	}
}


/* FIXME: I'm sure these functions should fail gracefully somehow :-) */
static MsOlePos
ms_ole_write_bb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8  *dest;
	gint32   lengthen;
	guint32  bytes   = length;
	int      offset  = s->position%BB_BLOCK_SIZE;
	guint32  blkidx  = s->position/BB_BLOCK_SIZE;
	
	s->file->dirty = 1;
	while (bytes > 0) {
		BLP block;
		int cpylen = BB_BLOCK_SIZE - offset;
		if (cpylen > bytes)
			cpylen = bytes;
		
		if (!s->blocks || blkidx >= s->blocks->len)
			ms_ole_append_block (s);

		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		
		dest = BB_W_PTR(s->file, block) + offset;

#if OLE_DEBUG > 1
		printf ("Copy %d bytes to block %d\n", cpylen, block);
#endif
		memcpy (dest, ptr, cpylen);
		ptr   += cpylen;
		bytes -= cpylen;
		
		offset = 0;
		blkidx++;
	}

	lengthen = s->position - s->size + length;
	if (lengthen > 0)
		s->size += lengthen;

	s->lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return length;
}


/* FIXME: I'm sure these functions should fail gracefully somehow :-) */
static MsOlePos
ms_ole_write_sb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8 *dest;
	int     offset  = s->position%SB_BLOCK_SIZE;
	guint32 blkidx  = s->position/SB_BLOCK_SIZE;
	guint32 bytes   = length;
	gint32  lengthen;
	
	s->file->dirty = 1;
	while (bytes > 0) {
		BLP block;
		int cpylen = SB_BLOCK_SIZE - offset;

		if (cpylen > bytes)
			cpylen = bytes;
		
		if (!s->blocks || blkidx >= s->blocks->len)
			ms_ole_append_block (s);
		g_assert (s->blocks);

		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		
		dest = GET_SB_W_PTR(s->file, block) + offset;
		
		g_assert (cpylen >= 0);

		memcpy (dest, ptr, cpylen);
		ptr   += cpylen;
		bytes -= cpylen;

		lengthen = s->position + length - bytes - s->size;
		if (lengthen > 0)
			s->size += lengthen;
		
		/* Must be exactly filling the block */
		if (s->size >= BB_THRESHOLD)
		{
			PPS         *p = s->pps;
			MsOlePos oldlen;
			guint8      *buffer;

			buffer       = g_new (guint8, s->size);
			s->lseek     (s, 0, MsOleSeekSet);
			oldlen       = s->size;
			s->read_copy (s, buffer, oldlen);

			free_allocation (s->file, p->start, 0);
			p->start    = END_OF_CHAIN;
#if OLE_DEBUG > 1
			printf ("\n\n--- Converting ---\n\n\n");
#endif
			s->read_copy = ms_ole_read_copy_bb;
			s->read_ptr  = ms_ole_read_ptr_bb;
			s->lseek     = ms_ole_lseek;
			s->tell      = tell_pos;
			s->write     = ms_ole_write_bb;

			g_assert (s->size % SB_BLOCK_SIZE == 0);

			/* Convert the file to BBlocks */
			s->size     = 0;
			s->position = 0;
			s->type  = MsOleLargeBlock;
			g_array_free (s->blocks, TRUE);
			s->blocks   = 0;

			s->write (s, buffer, oldlen);

			/* Continue the interrupted write */
			ms_ole_write_bb (s, ptr, bytes);
			bytes = 0;
#if OLE_DEBUG > 1
			printf ("\n\n--- Done ---\n\n\n");
#endif
			g_free (buffer);
			return length;
		}
		
		offset = 0;
		blkidx++;

		if (libole2_debug)
			check_stream (s);
	}
	s->lseek (s, length, MsOleSeekCur);

	return length;
}


/**
 * pps_create:
 * @f: ole file handle.
 * @p: returned pps.
 * @parent: parent pps.
 * @name: its name.
 * @type: the type.
 * 
 * Creates a storage or stream.
 * 
 * Return value: error status.
 **/
static MsOleErr
pps_create (MsOle *f, GList **p, GList *parent, const char *name,
	    MsOleType type)
{
	PPS *pps, *par;

	if (!p || !parent || !parent->data || !name) {
		g_warning ("duff arguments to pps_create");
		return MS_OLE_ERR_BADARG;
	}

	pps  = g_new (PPS, 1);
	if (!pps)
		return MS_OLE_ERR_MEM;
	
	pps->sig      = PPS_SIG;
	pps->name     = g_strdup (name);
	pps->type     = type;
	pps->size     = 0;
	pps->start    = END_OF_CHAIN;
	pps->children = NULL;
	pps->parent   = parent->data;
	
	par = (PPS *)parent->data;
	par->children = g_list_insert_sorted (par->children, pps,
					      (GCompareFunc)pps_compare_func);
	*p = g_list_find (par->children, pps);
	f->num_pps++;

	return MS_OLE_ERR_OK;
}


/**
 * find_in_pps:
 * @l: the parent storage chain element.
 * 
 * Find the right Stream ... John 4:13-14 ...
 * in a storage
 * 
 * Return value: %NULL if not found or pointer to the child list
 **/
static GList *
find_in_pps (GList *l, const char *name)
{
	PPS   *pps;
	GList *cur;

	g_return_val_if_fail (l != NULL, NULL);
	g_return_val_if_fail (l->data != NULL, NULL);
	pps = l->data;
	g_return_val_if_fail (IS_PPS (pps), NULL);
	
	if (pps->type == MsOleStorageT ||
	    pps->type == MsOleRootT)
		cur = pps->children;
	else {
		g_warning ("trying to enter a stream '%s'",
			   pps->name?pps->name:"no name");
		return NULL;
	}
	
	for ( ;cur ; cur = g_list_next (cur)) {
		PPS *pps = cur->data;
		g_return_val_if_fail (IS_PPS (pps), NULL);
		
		if (!pps->name)
			continue;
		
		if (!g_strcasecmp (pps->name, name))
			return cur;
	}
	return NULL;
}


/**
 * path_to_pps:
 * @pps:  pointer to pps to return value in.
 * @f:    ole file hande.
 * @path: path to find.
 * @file: file to find in path.
 * @create_if_not_found: create the pps with the given path if not found.
 * 
 * Locates a stream or storage with the given path.
 * 
 * Return value: a #MsOleErr code.
 **/
static MsOleErr
path_to_pps (PPS **pps, MsOle *f, const char *path,
	     const char *file,
	     gboolean create_if_not_found)
{
	guint     lp;
	gchar   **dirs;
	GList    *cur, *parent;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);
	
	dirs = g_strsplit (path, "/", -1);
	g_return_val_if_fail (dirs != NULL, MS_OLE_ERR_BADARG);

	parent = cur = f->pps;

	for (lp = 0; dirs[lp]; lp++) {
		if (dirs[lp][0] == '\0' || !cur) {
			g_free (dirs[lp]);
			continue;
		}

		parent = cur;

		cur = find_in_pps (parent, dirs[lp]);
		
		if (!cur && create_if_not_found &&
		    pps_create (f, &cur, parent, dirs[lp], MsOleStorageT) !=
		    MS_OLE_ERR_OK)
			cur = NULL;
		/* else carry on not finding them before dropping out */

		g_free (dirs[lp]);
	}
	g_free (dirs);

	if (!cur || !cur->data)
		return MS_OLE_ERR_EXIST;

	if (file[0] == '\0') { /* We just want a directory */
		*pps = cur->data;
		g_return_val_if_fail (IS_PPS (cur->data), MS_OLE_ERR_INVALID);
		return MS_OLE_ERR_OK;
	}

	parent = cur;
	cur = find_in_pps (parent, file);

	/* now the file */
	if (!cur) {
		if (create_if_not_found) {
			MsOleErr result;
			result = pps_create (f, &cur, parent, file,
					     MsOleStreamT);
			if (result == MS_OLE_ERR_OK) {
				*pps = cur->data;
				g_return_val_if_fail (IS_PPS (cur->data),
						      MS_OLE_ERR_INVALID);
				return MS_OLE_ERR_OK;
			} else
				return result;
		}
		return MS_OLE_ERR_EXIST;
	}

	if (cur && cur->data) {
		*pps = cur->data;
		g_return_val_if_fail (IS_PPS (cur->data), MS_OLE_ERR_INVALID);
		return MS_OLE_ERR_OK;
	}

	return MS_OLE_ERR_EXIST;
}


/**
 * ms_ole_unlink:
 * @fs: filesystem object.
 * @path: path of the stream or directory to delete.
 *
 * Delete the stream or directory @path on the filesystem @fs.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_unlink (MsOle *f, const char *path)
{
	g_warning ("Unimplemented");

	/* FIXME missing implementation, or at least a better error code =-) */
	return MS_OLE_ERR_NOTEMPTY;
}


/**
 * ms_ole_directory:
 * @names: array where the names are storesd, it's %NULL ended.
 * @fs: filesystem object.
 * @dirpath: directory path.
 *
 * Gets the names of the streams and directories in the directory @dirpath.
 *
 * Returns: a #MsOleErr code.
 **/
MsOleErr
ms_ole_directory (char ***names, MsOle *f, const char *path)
{
	char    **ans;
	PPS      *pps;
	MsOleErr  result;
	GList    *l;
	int       lp;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);

	if ((result = path_to_pps (&pps, f, path, "", FALSE)) !=
	    MS_OLE_ERR_OK)
		return result;

	if (!pps)
		return MS_OLE_ERR_INVALID;

	l   = pps->children;
	ans = g_new (char *, g_list_length (l) + 1);
	
	lp = 0;
	for (; l; l = g_list_next (l)) {
		pps = (PPS *)l->data;

		if (!pps->name)
			continue;

		ans[lp] = g_strdup (pps->name);
		lp++;
	}
	ans[lp] = NULL;

	*names = ans;
	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stat:
 * @stat: stat information.
 * @fs: filesystem object.
 * @dirpath: directory path.
 * @name: stream or directory name.
 *
 * Gets information about the stream or the directory which is in the directory
 * @dirpath and its name is @file.
 *
 * Returns: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stat (MsOleStat *stat, MsOle *f, const char *path,
	     const char *file)
{
	PPS      *pps;
	MsOleErr  result;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (file != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (stat != NULL, MS_OLE_ERR_BADARG);

	if ((result = path_to_pps (&pps, f, path, file, FALSE)) !=
	    MS_OLE_ERR_OK)
		return result;

	if (!pps)
		return MS_OLE_ERR_INVALID;

	stat->type = pps->type;
	stat->size = pps->size;

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_open:
 * @stream: stream object.
 * @fs: filesystem object.
 * @dirpath: directory of the stream.
 * @name: stream name.
 * @mode: mode of opening stream.
 * 
 * Opens the stream in @dirpath with the name @name and creates the stream
 * object @stream. If @mode is '%r' it opens read only, and if it is '%w'
 * it opens for write only.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_open (MsOleStream ** const stream, MsOle *f,
		    const char *path, const char *fname, char mode)
{
	PPS         *p;
	MsOleStream *s;
	int lp, panic=0;
	MsOleErr     result;

	if (!stream)
		return MS_OLE_ERR_BADARG;
	*stream = NULL;

	if (!path || !f)
		return MS_OLE_ERR_BADARG;

	if (mode == 'w' && f->mode != 'w') {
		printf ("Opening stream '%c' when file is '%c' only\n",
			mode, f->mode);
		return MS_OLE_ERR_PERM;
	}

	if ((result = path_to_pps (&p, f, path, fname, (mode == 'w'))) !=
	    MS_OLE_ERR_OK)
		return result;

	s           = g_new0 (MsOleStream, 1);
	s->file     = f;
	s->pps      = p;
	s->position = 0;
	s->size     = p->size;
	s->blocks   = NULL;

#if OLE_DEBUG > 0
	printf ("Parsing blocks\n");
#endif
	if (s->size >= BB_THRESHOLD) {
		BLP b = p->start;

		s->read_copy = ms_ole_read_copy_bb;
		s->read_ptr  = ms_ole_read_ptr_bb;
		s->lseek     = ms_ole_lseek;
		s->tell      = tell_pos;
		s->write     = ms_ole_write_bb;

		s->blocks    = g_array_new (FALSE, FALSE, sizeof(BLP));
		s->type   = MsOleLargeBlock;
		for (lp = 0; !panic & (lp < (s->size + BB_BLOCK_SIZE - 1) / BB_BLOCK_SIZE); lp++) {
			g_array_append_val (s->blocks, b);
#if OLE_DEBUG > 1
			printf ("Block %d\n", b);
#endif
			if (b == END_OF_CHAIN ||
			    b == SPECIAL_BLOCK ||
			    b == UNUSED_BLOCK) {

				printf ("Panic: broken stream, truncating to block %d\n", lp);
				s->size = (lp-1)*BB_BLOCK_SIZE;
				panic   = 1;

#if OLE_DEBUG > 0
				if (b == END_OF_CHAIN)
					printf ("Warning: bad file length in '%s'\n", p->name);
				else if (b == SPECIAL_BLOCK)
					printf ("Warning: special block in '%s'\n", p->name);
				else if (b == UNUSED_BLOCK)
					printf ("Warning: unused block in '%s'\n", p->name);
#endif
			} else
				b = NEXT_BB (f, b);
		}
		if (b != END_OF_CHAIN) {
			BLP next;
			printf ("Panic: extra unused blocks on end of '%s', wiping it\n",
				p->name);
			while (b != END_OF_CHAIN &&
			       b != UNUSED_BLOCK &&
			       b != SPECIAL_BLOCK) {
				next = NEXT_BB (f, b);
				g_array_index (f->bb, BLP, b) = END_OF_CHAIN;
				b = next;
			}
		}
	} else {
		BLP b = p->start;

		s->read_copy = ms_ole_read_copy_sb;
		s->read_ptr  = ms_ole_read_ptr_sb;
		s->lseek     = ms_ole_lseek;
		s->tell      = tell_pos;
		s->write     = ms_ole_write_sb;

		if (s->size>0)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));
		else
			s->blocks = NULL;

		s->type   = MsOleSmallBlock;

		for (lp = 0; !panic & (lp < (s->size + SB_BLOCK_SIZE - 1) / SB_BLOCK_SIZE); lp++) {
			g_array_append_val (s->blocks, b);
#if OLE_DEBUG > 0
			printf ("Block %d\n", b);
#endif
			if (b == END_OF_CHAIN ||
			    b == SPECIAL_BLOCK ||
			    b == UNUSED_BLOCK) {

				printf ("Panic: broken stream, truncating to block %d\n", lp);
				s->size = (lp-1)*SB_BLOCK_SIZE;
				panic   = 1;
#if OLE_DEBUG > 0
				if (b == END_OF_CHAIN)
					printf ("Warning: bad file length in '%s'\n", p->name);
				else if (b == SPECIAL_BLOCK)
					printf ("Warning: special block in '%s'\n", p->name);
				else if (b == UNUSED_BLOCK)
					printf ("Warning: unused block in '%s'\n", p->name);
#endif
			} else
				b = NEXT_SB (f, b);
		}
		if (b != END_OF_CHAIN) {
			BLP next;
			printf ("Panic: extra unused blocks on end of '%s', wiping it\n",
				p->name);
			while (b != END_OF_CHAIN &&
			       b != UNUSED_BLOCK &&
			       b != SPECIAL_BLOCK) {
				next = NEXT_SB (f, b);
				g_array_index (f->sb, BLP, b) = END_OF_CHAIN;
				b = next;
			}
			if (b != END_OF_CHAIN)
				printf ("Panic: even more serious block error\n");
		}
	}
	*stream = s;
	ms_ole_ref (s->file);

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_duplicate:
 * @stream_copy: stream object copy.
 * @stream: stream object to be duplicated.
 * 
 * Duplicates the stream object @stream.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_duplicate (MsOleStream **s, const MsOleStream * const stream)
{
	if (!s || !stream)
		return MS_OLE_ERR_BADARG;

	/* Lets face it having two pointers to the same file is a nightmare anyway :-) */
	g_warning ("Do NOT use this function, it is unsafe with the blocks array");

	if (!(*s = g_new (MsOleStream, 1)))
		return MS_OLE_ERR_MEM;

	memcpy (*s, stream, sizeof (MsOleStream));
	ms_ole_ref (stream->file);

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_close:
 * @stream: stream object to be closed.
 * 
 * Closes the @stream.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_close (MsOleStream **s)
{
	if (*s) {
		if ((*s)->file && (*s)->file->mode == 'w')
			((PPS *)(*s)->pps)->size = (*s)->size;

		if ((*s)->blocks)
			g_array_free ((*s)->blocks, TRUE);

		ms_ole_unref ((*s)->file);

		g_free (*s);
		*s = NULL;

		return MS_OLE_ERR_OK;
	}
	return MS_OLE_ERR_BADARG;
}

--- Added File ms-ole.h in package Packages/Products/DCProject ---
/**
 * ms-ole.h: MS Office OLE support for Gnumeric
 *
 * Authors:
 *    Michael Meeks (michael@imaginator.com)
 *    Arturo Tena (arturo@directmail.org)
 **/

#ifndef MS_OLE_H
#define MS_OLE_H

/* This should be done in glib */
#ifndef _WIN32
#	include <fcntl.h>	/* for mode_t */
#else
	typedef unsigned long mode_t;
	typedef size_t ssize_t;
	typedef /* signed */ long off_t;
#endif
#include <glib.h>

typedef enum {
	MS_OLE_ERR_OK,
	MS_OLE_ERR_EXIST,
	MS_OLE_ERR_INVALID,
	MS_OLE_ERR_FORMAT,
	MS_OLE_ERR_PERM,
	MS_OLE_ERR_MEM,
	MS_OLE_ERR_SPACE,
	MS_OLE_ERR_NOTEMPTY,
	MS_OLE_ERR_BADARG
} MsOleErr;

typedef enum {
	MsOleSeekSet,
	MsOleSeekCur,
	MsOleSeekEnd
} MsOleSeek;

typedef enum  {
	MsOleStorageT = 1,
	MsOleStreamT  = 2,
	MsOleRootT    = 5
} MsOleType;

typedef guint32 MsOlePos;
typedef gint32  MsOleSPos;
/*
#ifdef G_HAVE_GINT64
	typedef guint64 MsOlePos;
	typedef gint64  MsOleSPos;
#else
	typedef guint32 MsOlePos;
	typedef gint32  MsOleSPos;
#endif
*/


typedef struct _MsOle             MsOle;
typedef struct _MsOleStat         MsOleStat;
typedef struct _MsOleStream       MsOleStream;
typedef struct _MsOleSysWrappers  MsOleSysWrappers;

struct _MsOleSysWrappers {
	int     (*open2)	(const char *pathname, int flags);
	int     (*open3)	(const char *pathname, int flags, mode_t mode);
	ssize_t (*read)		(int fd, void *buf, size_t count);
	int     (*close)	(int fd);
	ssize_t (*write)	(int fd, const void *buf, size_t count);
	off_t   (*lseek)	(int fd, off_t offset, int whence);
	int     (*isregfile)	(int fd);
	int     (*getfilesize)	(int fd, guint32 *size);
};

struct _MsOleStat {
	MsOleType type;
	MsOlePos  size;
};

#define                 ms_ole_open(fs,path)     ms_ole_open_vfs ((fs), (path), TRUE, NULL)
extern MsOleErr		ms_ole_open_vfs		(MsOle **fs,
						 const char *path,
						 gboolean try_mmap,
						 MsOleSysWrappers *wrappers);
#define                 ms_ole_create(fs,path)   ms_ole_create_vfs ((fs), (path), TRUE, NULL)
extern MsOleErr		ms_ole_create_vfs	(MsOle **fs,
						 const char *path,
						 int try_mmap,
						 MsOleSysWrappers *wrappers);
extern void		ms_ole_destroy		(MsOle **fs);
extern MsOleErr		ms_ole_unlink		(MsOle *fs,
						 const char *path);
extern MsOleErr		ms_ole_directory	(char ***names,
						 MsOle *fs,
						 const char *dirpath);
extern MsOleErr		ms_ole_stat		(MsOleStat *stat,
						 MsOle *fs,
						 const char *dirpath,
						 const char *name);

struct _MsOleStream {

	MsOlePos size;

	gint		(*read_copy)	(MsOleStream *stream,
					 guint8 *ptr,
					 MsOlePos length);

	guint8 *	(*read_ptr)	(MsOleStream *stream,
					 MsOlePos length);

	MsOleSPos	(*lseek)	(MsOleStream *stream,
					 MsOleSPos bytes,
					 MsOleSeek type);

	MsOlePos	(*tell)		(MsOleStream *stream);

	MsOlePos	(*write)	(MsOleStream *stream,
					 guint8 *ptr,
					 MsOlePos length);


	/**
	 * Private.
	 **/
	enum {
		MsOleSmallBlock,
		MsOleLargeBlock
	} type;
	MsOle		*file;
	void		*pps;		/* Straight PPS */
	GArray		*blocks;	/* A list of the blocks in the file
					   if NULL: no file */
	MsOlePos	 position;	/* Current offset into file.
					   Points to the next byte to read */
};

#define MS_OLE_GET_GUINT8(p)  (*((const guint8 *)(p) + 0))
#define MS_OLE_GET_GUINT16(p) (guint16)(*((const guint8 *)(p)+0) |        \
					(*((const guint8 *)(p)+1)<<8))
#define MS_OLE_GET_GUINT32(p) (guint32)(*((const guint8 *)(p)+0) |        \
					(*((const guint8 *)(p)+1)<<8) |   \
					(*((const guint8 *)(p)+2)<<16) |  \
					(*((const guint8 *)(p)+3)<<24))
#define MS_OLE_GET_GUINT64(p) (MS_OLE_GET_GUINT32(p) | \
			       (((guint32)MS_OLE_GET_GUINT32((const guint8 *)(p)+4))<<32))

#define MS_OLE_SET_GUINT8(p,n)  (*((guint8 *)(p) + 0) = n)
#define MS_OLE_SET_GUINT16(p,n) ((*((guint8 *)(p)+0)=((n)&0xff)),         \
				 (*((guint8 *)(p)+1)=((n)>>8)&0xff))
#define MS_OLE_SET_GUINT32(p,n) ((*((guint8 *)(p)+0)=((n))&0xff),         \
				 (*((guint8 *)(p)+1)=((n)>>8)&0xff),      \
				 (*((guint8 *)(p)+2)=((n)>>16)&0xff),     \
				 (*((guint8 *)(p)+3)=((n)>>24)&0xff))

extern MsOleErr		ms_ole_stream_open	(MsOleStream ** const stream,
						 MsOle *fs,
						 const char *dirpath,
						 const char *name,
						 char mode);
extern MsOleErr		ms_ole_stream_close	(MsOleStream ** const stream);
extern MsOleErr		ms_ole_stream_duplicate	(MsOleStream ** const stream_copy,
						 const MsOleStream *
						 const stream);

extern void		ms_ole_dump		(guint8 const *ptr,
						 guint32 len);

extern void		ms_ole_ref		(MsOle *fs);
extern void		ms_ole_unref		(MsOle *fs);
extern void		ms_ole_debug		(MsOle *fs,
						 int magic);



#endif	/* MS_OLE_H */

--- Added File ole.c in package Packages/Products/DCProject ---
//----------------------------------------------------------------------------//
// ole.c
// John Platten
// 01.12.01
//----------------------------------------------------------------------------//

#include <stdio.h>
#include "ms-ole.h"
#include "ms-ole-summary.h"

//----------------------------------------------------------------------------//
// GLOBALS
//----------------------------------------------------------------------------//

char str[512];

//----------------------------------------------------------------------------//
// PROTOTYPES
//----------------------------------------------------------------------------//

MsOle * _ms_ole_open (char *fileName);

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len);

void _ms_ole_destroy (MsOle *ole);

MsOleSummary * _ms_ole_summary_open (MsOle *ole);

void _ms_ole_summary_close (MsOleSummary *summary);

MsOleSummary * _ms_ole_docsummary_open (MsOle *f);

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId);

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId);

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId);

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId);

//----------------------------------------------------------------------------//

MsOle * _ms_ole_open (char *fileName)
{
	MsOle *ole = NULL;
	
	ms_ole_open_vfs(&ole, fileName, FALSE, NULL);
	
	// if (!ole) fprintf(stderr, ":ERROR:ms_ole_open_vfs:msOleErr:%x\n", ole);
		
	return ole;
}

//----------------------------------------------------------------------------//

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len)
{
	MsOle * msOle = NULL;
	MsOleErr msOleErr;
	char * fileName = "junk";
			
	msOleErr = ms_ole_open_vfs_body(&msOle, fileName, FALSE, NULL, jp_buf_ptr, jp_buf_len);
	
	// if (!msOle) fprintf(stderr, ":ERROR:ms_ole_open_vfs:msOleErr:%x\n", msOleErr);

	return msOle;
}

//----------------------------------------------------------------------------//

void _ms_ole_destroy (MsOle *ole)
{
    MsOle * myOle = ole;
    
    ms_ole_destroy(&myOle);
}

//----------------------------------------------------------------------------//

MsOleSummary * _ms_ole_summary_open (MsOle *ole)
{
	MsOleSummary *summary = NULL;
	
	summary = ms_ole_summary_open(ole);
	
	// if (!summary) fprintf(stderr, ":ERROR:_ms_ole_summary_open:summary:%x\n", summary);
		
	return summary;
}

//----------------------------------------------------------------------------//

void _ms_ole_summary_close (MsOleSummary *summary)
{
    ms_ole_summary_close(summary);
}

//----------------------------------------------------------------------------//

MsOleSummary * _ms_ole_docsummary_open (MsOle *f)
{
    return ms_ole_docsummary_open(f);
}

//----------------------------------------------------------------------------//

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId)
{
	char *str = NULL;
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_string(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId)
{
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_long(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId)
{
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_short(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId)
{
	GTimeVal timeval;
	gboolean ret = FALSE;
	
	timeval = ms_ole_summary_get_time(summary, propertyId, &ret);

	strcpy(str, ctime(&timeval.tv_sec));
	str[strlen(str)-1] = '\0';

	return str;
}

//----------------------------------------------------------------------------//

--- Added File ole.i in package Packages/Products/DCProject ---
%module ole
%{
#include "ms-ole.h"
#include "ms-ole-summary.h"
%}

MsOle * _ms_ole_open (char *fileName);

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len);

void _ms_ole_destroy (MsOle *ole);

MsOleSummary * _ms_ole_summary_open (MsOle *ole);

void _ms_ole_summary_close (MsOleSummary *summary);

MsOleSummary * _ms_ole_docsummary_open (MsOle *f);

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId);

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId);

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId);

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId);

--- Added File ole_wrap.c in package Packages/Products/DCProject ---
/*
 * FILE : ole_wrap.c
 * 
 * This file was automatically generated by :
 * Simplified Wrapper and Interface Generator (SWIG)
 * Version 1.1 (Build 883)
 * 
 * Portions Copyright (c) 1995-1998
 * The University of Utah and The Regents of the University of California.
 * Permission is granted to distribute this file in any manner provided
 * this notice remains intact.
 * 
 * Do not make changes to this file--changes will be lost!
 *
 */


#define SWIGCODE
/* Implementation : PYTHON */

#define SWIGPYTHON
/***********************************************************************
 * $Header: /cvs-repository/Packages/Products/DCProject/CMF_MS_Files/src/libole/ole_wrap.c,v 1.1 2001/05/30 14:39:19 jack Exp $
 * swig_lib/python/python.cfg
 *
 * Contains variable linking and pointer type-checking code.
 ************************************************************************/

#include <string.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif
#include "Python.h"

/* Definitions for Windows/Unix exporting */
#if defined(_WIN32) || defined(__WIN32__)
#   if defined(_MSC_VER)
#	define SWIGEXPORT(a) __declspec(dllexport) a
#   else
#	if defined(__BORLANDC__)
#	    define SWIGEXPORT(a) a _export
#	else
#	    define SWIGEXPORT(a) a
#	endif
#   endif
#else
#   define SWIGEXPORT(a) a
#endif

#ifdef SWIG_GLOBAL
#define SWIGSTATICRUNTIME(a) SWIGEXPORT(a)
#else
#define SWIGSTATICRUNTIME(a) static a
#endif

typedef struct {
  char  *name;
  PyObject *(*get_attr)(void);
  int (*set_attr)(PyObject *);
} swig_globalvar;

typedef struct swig_varlinkobject {
  PyObject_HEAD
  swig_globalvar **vars;
  int      nvars;
  int      maxvars;
} swig_varlinkobject;

/* ----------------------------------------------------------------------
   swig_varlink_repr()

   Function for python repr method
   ---------------------------------------------------------------------- */

static PyObject *
swig_varlink_repr(swig_varlinkobject *v)
{
  v = v;
  return PyString_FromString("<Global variables>");
}

/* ---------------------------------------------------------------------
   swig_varlink_print()

   Print out all of the global variable names
   --------------------------------------------------------------------- */

static int
swig_varlink_print(swig_varlinkobject *v, FILE *fp, int flags)
{

  int i = 0;
  flags = flags;
  fprintf(fp,"Global variables { ");
  while (v->vars[i]) {
    fprintf(fp,"%s", v->vars[i]->name);
    i++;
    if (v->vars[i]) fprintf(fp,", ");
  }
  fprintf(fp," }\n");
  return 0;
}

/* --------------------------------------------------------------------
   swig_varlink_getattr
 
   This function gets the value of a variable and returns it as a
   PyObject.   In our case, we'll be looking at the datatype and
   converting into a number or string
   -------------------------------------------------------------------- */

static PyObject *
swig_varlink_getattr(swig_varlinkobject *v, char *n)
{
  int i = 0;
  char temp[128];

  while (v->vars[i]) {
    if (strcmp(v->vars[i]->name,n) == 0) {
      return (*v->vars[i]->get_attr)();
    }
    i++;
  }
  sprintf(temp,"C global variable %s not found.", n);
  PyErr_SetString(PyExc_NameError,temp);
  return NULL;
}

/* -------------------------------------------------------------------
   swig_varlink_setattr()

   This function sets the value of a variable.
   ------------------------------------------------------------------- */

static int
swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p)
{
  char temp[128];
  int i = 0;
  while (v->vars[i]) {
    if (strcmp(v->vars[i]->name,n) == 0) {
      return (*v->vars[i]->set_attr)(p);
    }
    i++;
  }
  sprintf(temp,"C global variable %s not found.", n);
  PyErr_SetString(PyExc_NameError,temp);
  return 1;
}

statichere PyTypeObject varlinktype = {
/*  PyObject_HEAD_INIT(&PyType_Type)  Note : This doesn't work on some machines */
  PyObject_HEAD_INIT(0)              
  0,
  "varlink",                          /* Type name    */
  sizeof(swig_varlinkobject),         /* Basic size   */
  0,                                  /* Itemsize     */
  0,                                  /* Deallocator  */ 
  (printfunc) swig_varlink_print,     /* Print        */
  (getattrfunc) swig_varlink_getattr, /* get attr     */
  (setattrfunc) swig_varlink_setattr, /* Set attr     */
  0,                                  /* tp_compare   */
  (reprfunc) swig_varlink_repr,       /* tp_repr      */    
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_mapping*/
  0,                                  /* tp_hash      */
};

/* Create a variable linking object for use later */

SWIGSTATICRUNTIME(PyObject *)
SWIG_newvarlink(void)
{
  swig_varlinkobject *result = 0;
  result = PyMem_NEW(swig_varlinkobject,1);
  varlinktype.ob_type = &PyType_Type;    /* Patch varlinktype into a PyType */
  result->ob_type = &varlinktype;
  /*  _Py_NewReference(result);  Does not seem to be necessary */
  result->nvars = 0;
  result->maxvars = 64;
  result->vars = (swig_globalvar **) malloc(64*sizeof(swig_globalvar *));
  result->vars[0] = 0;
  result->ob_refcnt = 0;
  Py_XINCREF((PyObject *) result);
  return ((PyObject*) result);
}

SWIGSTATICRUNTIME(void)
SWIG_addvarlink(PyObject *p, char *name,
	   PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p))
{
  swig_varlinkobject *v;
  v= (swig_varlinkobject *) p;
	
  if (v->nvars >= v->maxvars -1) {
    v->maxvars = 2*v->maxvars;
    v->vars = (swig_globalvar **) realloc(v->vars,v->maxvars*sizeof(swig_globalvar *));
    if (v->vars == NULL) {
      fprintf(stderr,"SWIG : Fatal error in initializing Python module.\n");
      exit(1);
    }
  }
  v->vars[v->nvars] = (swig_globalvar *) malloc(sizeof(swig_globalvar));
  v->vars[v->nvars]->name = (char *) malloc(strlen(name)+1);
  strcpy(v->vars[v->nvars]->name,name);
  v->vars[v->nvars]->get_attr = get_attr;
  v->vars[v->nvars]->set_attr = set_attr;
  v->nvars++;
  v->vars[v->nvars] = 0;
}

/* -----------------------------------------------------------------------------
 * Pointer type-checking
 * ----------------------------------------------------------------------------- */

/* SWIG pointer structure */
typedef struct SwigPtrType {
  char               *name;               /* Datatype name                  */
  int                 len;                /* Length (used for optimization) */
  void               *(*cast)(void *);    /* Pointer casting function       */
  struct SwigPtrType *next;               /* Linked list pointer            */
} SwigPtrType;

/* Pointer cache structure */
typedef struct {
  int                 stat;               /* Status (valid) bit             */
  SwigPtrType        *tp;                 /* Pointer to type structure      */
  char                name[256];          /* Given datatype name            */
  char                mapped[256];        /* Equivalent name                */
} SwigCacheType;

static int SwigPtrMax  = 64;           /* Max entries that can be currently held */
static int SwigPtrN    = 0;            /* Current number of entries              */
static int SwigPtrSort = 0;            /* Status flag indicating sort            */
static int SwigStart[256];             /* Starting positions of types            */
static SwigPtrType *SwigPtrTable = 0;  /* Table containing pointer equivalences  */

/* Cached values */
#define SWIG_CACHESIZE  8
#define SWIG_CACHEMASK  0x7
static SwigCacheType SwigCache[SWIG_CACHESIZE];  
static int SwigCacheIndex = 0;
static int SwigLastCache = 0;

/* Sort comparison function */
static int swigsort(const void *data1, const void *data2) {
	SwigPtrType *d1 = (SwigPtrType *) data1;
	SwigPtrType *d2 = (SwigPtrType *) data2;
	return strcmp(d1->name,d2->name);
}

/* Register a new datatype with the type-checker */
SWIGSTATICRUNTIME(void) 
SWIG_RegisterMapping(char *origtype, char *newtype, void *(*cast)(void *)) {
  int i;
  SwigPtrType *t = 0,*t1;

  /* Allocate the pointer table if necessary */
  if (!SwigPtrTable) {     
    SwigPtrTable = (SwigPtrType *) malloc(SwigPtrMax*sizeof(SwigPtrType));
  }

  /* Grow the table */
  if (SwigPtrN >= SwigPtrMax) {
    SwigPtrMax = 2*SwigPtrMax;
    SwigPtrTable = (SwigPtrType *) realloc((char *) SwigPtrTable,SwigPtrMax*sizeof(SwigPtrType));
  }
  for (i = 0; i < SwigPtrN; i++) {
    if (strcmp(SwigPtrTable[i].name,origtype) == 0) {
      t = &SwigPtrTable[i];
      break;
    }
  }
  if (!t) {
    t = &SwigPtrTable[SwigPtrN++];
    t->name = origtype;
    t->len = strlen(t->name);
    t->cast = 0;
    t->next = 0;
  }

  /* Check for existing entries */
  while (t->next) {
    if ((strcmp(t->name,newtype) == 0)) {
      if (cast) t->cast = cast;
      return;
    }
    t = t->next;
  }
  t1 = (SwigPtrType *) malloc(sizeof(SwigPtrType));
  t1->name = newtype;
  t1->len = strlen(t1->name);
  t1->cast = cast;
  t1->next = 0;            
  t->next = t1;           
  SwigPtrSort = 0;
}

/* Make a pointer value string */
SWIGSTATICRUNTIME(void) 
SWIG_MakePtr(char *c, const void *ptr, char *type) {
  static char hex[17] = "0123456789abcdef";
  unsigned long p, s;
  char result[24], *r; 
  r = result;
  p = (unsigned long) ptr;
  if (p > 0) {
    while (p > 0) {
      s = p & 0xf;
      *(r++) = hex[s];
      p = p >> 4;
    }
    *r = '_';
    while (r >= result)
      *(c++) = *(r--);
    strcpy (c, type);
  } else {
    strcpy (c, "NULL");
  }
}

/* Function for getting a pointer value */
SWIGSTATICRUNTIME(char *) 
SWIG_GetPtr(char *c, void **ptr, char *t)
{
  unsigned long p;
  char temp_type[256], *name;
  int  i, len, start, end;
  SwigPtrType *sp,*tp;
  SwigCacheType *cache;
  register int d;

  p = 0;
  /* Pointer values must start with leading underscore */
  if (*c != '_') {
    *ptr = (void *) 0;
    if (strcmp(c,"NULL") == 0) return (char *) 0;
    else c;
  }
  c++;
  /* Extract hex value from pointer */
  while (d = *c) {
    if ((d >= '0') && (d <= '9'))
      p = (p << 4) + (d - '0');
    else if ((d >= 'a') && (d <= 'f'))
      p = (p << 4) + (d - ('a'-10));
    else
      break; 
    c++;
  }
  *ptr = (void *) p;
  if ((!t) || (strcmp(t,c)==0)) return (char *) 0;

  if (!SwigPtrSort) {
    qsort((void *) SwigPtrTable, SwigPtrN, sizeof(SwigPtrType), swigsort); 
    for (i = 0; i < 256; i++) SwigStart[i] = SwigPtrN;
    for (i = SwigPtrN-1; i >= 0; i--) SwigStart[(int) (SwigPtrTable[i].name[1])] = i;
    for (i = 255; i >= 1; i--) {
      if (SwigStart[i-1] > SwigStart[i])
	SwigStart[i-1] = SwigStart[i];
    }
    SwigPtrSort = 1;
    for (i = 0; i < SWIG_CACHESIZE; i++) SwigCache[i].stat = 0;
  }
  /* First check cache for matches.  Uses last cache value as starting point */
  cache = &SwigCache[SwigLastCache];
  for (i = 0; i < SWIG_CACHESIZE; i++) {
    if (cache->stat && (strcmp(t,cache->name) == 0) && (strcmp(c,cache->mapped) == 0)) {
      cache->stat++;
      if (cache->tp->cast) *ptr = (*(cache->tp->cast))(*ptr);
      return (char *) 0;
    }
    SwigLastCache = (SwigLastCache+1) & SWIG_CACHEMASK;
    if (!SwigLastCache) cache = SwigCache;
    else cache++;
  }
  /* Type mismatch.  Look through type-mapping table */
  start = SwigStart[(int) t[1]];
  end = SwigStart[(int) t[1]+1];
  sp = &SwigPtrTable[start];

  /* Try to find a match */
  while (start <= end) {
    if (strncmp(t,sp->name,sp->len) == 0) {
      name = sp->name;
      len = sp->len;
      tp = sp->next;
      /* Try to find entry for our given datatype */
      while(tp) {
	if (tp->len >= 255) {
	  return c;
	}
	strcpy(temp_type,tp->name);
	strncat(temp_type,t+len,255-tp->len);
	if (strcmp(c,temp_type) == 0) {
	  strcpy(SwigCache[SwigCacheIndex].mapped,c);
	  strcpy(SwigCache[SwigCacheIndex].name,t);
	  SwigCache[SwigCacheIndex].stat = 1;
	  SwigCache[SwigCacheIndex].tp = tp;
	  SwigCacheIndex = SwigCacheIndex & SWIG_CACHEMASK;
	  /* Get pointer value */
	  *ptr = (void *) p;
	  if (tp->cast) *ptr = (*(tp->cast))(*ptr);
	  return (char *) 0;
	}
	tp = tp->next;
      }
    }
    sp++;
    start++;
  }
  return c;
} 

/* New object-based GetPointer function. This uses the Python abstract
 * object interface to automatically dereference the 'this' attribute
 * of shadow objects. */

SWIGSTATICRUNTIME(char *)
SWIG_GetPtrObj(PyObject *obj, void **ptr, char *type) {
  PyObject *sobj = obj;
  char     *str;
  if (!PyString_Check(obj)) {
    sobj = PyObject_GetAttrString(obj,"this");
    if (!sobj) return "";
  }
  str = PyString_AsString(sobj);
  return SWIG_GetPtr(str,ptr,type);
}

#ifdef __cplusplus
}
#endif


#define SWIG_init    initole

#define SWIG_name    "ole"

#include "ms-ole.h"
#include "ms-ole-summary.h"
#ifdef __cplusplus
extern "C" {
#endif
static PyObject *_wrap__ms_ole_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _result;
    char * _arg0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"s:_ms_ole_open",&_arg0)) 
        return NULL;
    _result = (MsOle *)_ms_ole_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOle_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_open_body(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _result;
    char * _arg0;
    int  _arg1;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"s#:_ms_ole_open_body",&_arg0,&_arg1)) 
        return NULL;
    _result = (MsOle *)_ms_ole_open_body(_arg0,_arg1);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOle_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_destroy(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _arg0;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_destroy",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_destroy. Expected _MsOle_p.");
        return NULL;
        }
    }
    _ms_ole_destroy(_arg0);
    Py_INCREF(Py_None);
    _resultobj = Py_None;
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _result;
    MsOle * _arg0;
    PyObject * _argo0 = 0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_summary_open",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_open. Expected _MsOle_p.");
        return NULL;
        }
    }
    _result = (MsOleSummary *)_ms_ole_summary_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOleSummary_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_close(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _arg0;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_summary_close",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_close. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _ms_ole_summary_close(_arg0);
    Py_INCREF(Py_None);
    _resultobj = Py_None;
    return _resultobj;
}

static PyObject *_wrap__ms_ole_docsummary_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _result;
    MsOle * _arg0;
    PyObject * _argo0 = 0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_docsummary_open",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_docsummary_open. Expected _MsOle_p.");
        return NULL;
        }
    }
    _result = (MsOleSummary *)_ms_ole_docsummary_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOleSummary_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_string(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    char * _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_string",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_string. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (char *)_ms_ole_summary_get_string(_arg0,_arg1);
    _resultobj = Py_BuildValue("s", _result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_long(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    int  _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_long",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_long. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (int )_ms_ole_summary_get_long(_arg0,_arg1);
    _resultobj = Py_BuildValue("i",_result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_short(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    short  _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_short",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_short. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (short )_ms_ole_summary_get_short(_arg0,_arg1);
    _resultobj = Py_BuildValue("h",_result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_time(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    char * _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_time",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_time. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (char *)_ms_ole_summary_get_time(_arg0,_arg1);
    _resultobj = Py_BuildValue("s", _result);
    return _resultobj;
}

static PyMethodDef oleMethods[] = {
	 { "_ms_ole_summary_get_time", _wrap__ms_ole_summary_get_time, METH_VARARGS },
	 { "_ms_ole_summary_get_short", _wrap__ms_ole_summary_get_short, METH_VARARGS },
	 { "_ms_ole_summary_get_long", _wrap__ms_ole_summary_get_long, METH_VARARGS },
	 { "_ms_ole_summary_get_string", _wrap__ms_ole_summary_get_string, METH_VARARGS },
	 { "_ms_ole_docsummary_open", _wrap__ms_ole_docsummary_open, METH_VARARGS },
	 { "_ms_ole_summary_close", _wrap__ms_ole_summary_close, METH_VARARGS },
	 { "_ms_ole_summary_open", _wrap__ms_ole_summary_open, METH_VARARGS },
	 { "_ms_ole_destroy", _wrap__ms_ole_destroy, METH_VARARGS },
	 { "_ms_ole_open_body", _wrap__ms_ole_open_body, METH_VARARGS },
	 { "_ms_ole_open", _wrap__ms_ole_open, METH_VARARGS },
	 { NULL, NULL }
};
#ifdef __cplusplus
}
#endif
/*
 * This table is used by the pointer type-checker
 */
static struct { char *n1; char *n2; void *(*pcnv)(void *); } _swig_mapping[] = {
    { "_signed_long","_long",0},
    { "_long","_unsigned_long",0},
    { "_long","_signed_long",0},
    { "_unsigned_long","_long",0},
    { "_signed_int","_int",0},
    { "_unsigned_short","_short",0},
    { "_signed_short","_short",0},
    { "_unsigned_int","_int",0},
    { "_short","_unsigned_short",0},
    { "_short","_signed_short",0},
    { "_int","_unsigned_int",0},
    { "_int","_signed_int",0},
{0,0,0}};

static PyObject *SWIG_globals;
#ifdef __cplusplus
extern "C" 
#endif
SWIGEXPORT(void) initole() {
	 PyObject *m, *d;
	 SWIG_globals = SWIG_newvarlink();
	 m = Py_InitModule("ole", oleMethods);
	 d = PyModule_GetDict(m);
{
   int i;
   for (i = 0; _swig_mapping[i].n1; i++)
        SWIG_RegisterMapping(_swig_mapping[i].n1,_swig_mapping[i].n2,_swig_mapping[i].pcnv);
}
}

--- Added File setup.py in package Packages/Products/DCProject ---
#!/usr/bin/env python

from distutils.core import setup, Extension

setup(name = "ole",
      version = "1.0", 
      description = "libole2 Interface",
      author = "John Platten",
      author_email = "jplatten@digicool.com",
      ext_modules =
          [('ole',
          { 'sources':
             ['ole_wrap.c',
              'ole.c',
              'version.c',
              'ms-ole.c',
              'ms-ole-vba.c',
              'ms-ole-summary.c'],
            'include_dirs': ['.'],
            'includes':
             ['config.h',
              'glibconfig.h',
              'libole2.h',
              'ms-ole.h',
              'ms-ole-vba.h',
              'ms-ole-summary.h'],
            'libraries': ["glib"],
          }
          )]

      )

--- Added File test-ole.c in package Packages/Products/DCProject ---
/**
 * test-ole.c: OLE2 file format helper program,
 *             good for dumping OLE streams, and
 * corresponding biff records, and hopefuly
 * some more ...
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 **/

#define TEST_DEBUG 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* For ctime() */
#include <time.h>

#include <ms-ole.h>
#include <ms-ole-vba.h>
#include <ms-ole-summary.h>

#define BIFF_TYPES_FILE    "biff-types.h"

#if TEST_DEBUG > 0
static char delim[]=":";
#else
static char delim[]=" ";
#endif
static char **arg_data = NULL;
static int    arg_cur  = 0;

typedef struct {
	guint16 opcode;
	char *name;
} GENERIC_TYPE;

static GPtrArray *biff_types   = NULL;

static char *cur_dir = NULL;

static void
read_types (char *fname, GPtrArray **types)
{
	FILE *file = fopen(fname, "r");
	char buffer[1024];
	*types = g_ptr_array_new ();
	if (!file) {
		char *newname = g_strconcat ("../", fname, NULL);
		file = fopen (newname, "r");
	}
	if (!file) {
		printf ("Can't find vital file '%s'\n", fname);
		return;
	}
	while (!feof(file)) {
		char *p;
		fgets(buffer,1023,file);
		for (p=buffer;*p;p++)
			if (*p=='0' && *(p+1)=='x') {
				GENERIC_TYPE *bt = g_new (GENERIC_TYPE,1);
				char *name, *pt;
				bt->opcode=strtol(p+2,0,16);
				pt = buffer;
				while (*pt && *pt != '#') pt++;      /* # */
				while (*pt && !isspace(*pt)) pt++;  /* define */
				while (*pt &&  isspace(*pt)) pt++;  /* '   ' */
				while (*pt && *pt != '_') pt++;     /* BIFF_ */
				name = *pt?pt+1:pt;
				while (*pt && !isspace(*pt)) pt++;
				bt->name=g_strndup(name, (pt-name));
				g_ptr_array_add (*types, bt);
				break;
			}
	}
	fclose (file);
}

static char*
get_biff_opcode_name (guint16 opcode)
{
	int lp;
	if (!biff_types)
		read_types (BIFF_TYPES_FILE, &biff_types);
	/* Count backwars to give preference to non-filtered record types */
	for (lp=biff_types->len; --lp >= 0 ;) {
		GENERIC_TYPE *bt = g_ptr_array_index (biff_types, lp);
		if (bt->opcode>0xff) {
			if (bt->opcode == opcode)
				return bt->name;
		} else {
			if (bt->opcode == (opcode&0xff))
				return bt->name;
		}
	}
	return "Unknown";
}

static void
list_files (MsOle *ole)
{
	char     **names;
	MsOleErr   result;
	int        lp;

	result = ms_ole_directory (&names, ole, cur_dir);
	if (result != MS_OLE_ERR_OK) {
		g_warning ("Failed dir");
		return;
	}

	if (!names[0])
		printf ("Empty directory\n");

	for (lp = 0; names[lp]; lp++) {
		MsOleStat s;
		result = ms_ole_stat (&s, ole, cur_dir, names[lp]);

		if (s.type == MsOleStreamT)
			if (names[lp][0] < 0x30) {
				printf ("'\\%x%s' : length %d bytes\n",
					names[lp][0], names[lp] + 1, s.size);
			} else {
				printf ("'%s : length %d bytes\n",
					names[lp], s.size);
			}
		else
			if (names[lp][0] < 0x30) {
				printf ("'\\%d[%s]' : Storage ( directory )\n",
					names[lp][0], names [lp] + 1);
			} else {
				printf ("'[%s] : Storage ( directory )\n",
					names [lp]);
			}
		
	}
}

static void
list_commands ()
{
	printf ("command can be one or all of:\n");
	printf (" * ls:                   list files\n");
	printf (" * cd:                   enter storage\n");
	printf (" * biff    <stream name>:   dump biff records, merging continues\n");
	printf (" * biffraw <stream name>:   dump biff records no merge + raw data\n");
	printf (" * dump    <stream name>:   dump stream\n");
	printf (" * summary              :   dump summary info\n");
	printf (" * docsummary           :   dump document summary info\n");
	printf (" * debug                :   dump internal ole library status\n");
	printf (" * tree                 :   dump the tree\n");
	printf (" * vba                  :   attempt to dump vba \n");
	printf (" Raw transfer commands\n");
	printf (" * get     <stream name> <fname>\n");
	printf (" * put     <fname> <stream name>\n");
	printf (" * copyin  [<fname>,]...\n");
	printf (" * copyout [<fname>,]...\n");
	printf (" * quit,exit,bye:        exit\n");
}

static void
syntax_error (char *err)
{
	if (err) {
		printf("Error: '%s'\n",err);
		exit(1);
	}
		
	printf ("Sytax:\n");
	printf (" ole <ole-file> [-i] [commands...]\n\n");
	printf (" -i: Interactive, queries for fresh commands\n\n");

	list_commands ();
	exit(1);
}

/* ---------------------------- End cut ---------------------------- */

static gboolean
simple_regexp (const char *regexp, const char *fname)
{
	int      i;
	gboolean ret = TRUE;

	g_return_val_if_fail (fname != NULL, FALSE);
	g_return_val_if_fail (regexp != NULL, FALSE);

	for (i = 0; regexp [i] && fname [i]; i++) {
		if (regexp [i] == '.')
			continue;

		if (toupper (regexp [i]) != toupper (fname [i])) {
			ret = FALSE;
			break;
		}
	}

	if (regexp [i] && regexp [i] == '*')
		ret = TRUE;

	else if (!regexp [i] && fname [i])
		ret = FALSE;

/*	if (ret)
	printf ("'%s' matched '%s'\n", regexp, fname);*/

	return ret;
}

/*
 * This should take a path to check the directory out there.
 */ 
static char *
get_regexp_name (const char *regexp, const char *path, MsOle *ole)
{
	char      *res = NULL;
	char     **names;
	MsOleErr   result;
	int        lp;

	result = ms_ole_directory (&names, ole, path);
	if (result != MS_OLE_ERR_OK) {
		g_warning ("Failed dir");
		return NULL;
	}

	if (!names [0])
		printf ("Empty directory\n");

	for (lp = 0; names[lp]; lp++) {
		if (simple_regexp (regexp, names [lp])) {
			res = g_strdup (names [lp]);
			break;
		}
	}

	return res;
}

static void
enter_dir (MsOle *ole)
{
	char *newpath, *ptr, *p;

	p = arg_data [arg_cur++];

	if (!p) {
		printf ("Takes a directory argument\n");
		return;
	}

	if (!g_strcasecmp (p, "..")) {
		guint lp;
		char **tmp;
		GString *newp = g_string_new ("");

		tmp = g_strsplit (cur_dir, "/", -1);
		lp  = 0;
		if (!tmp[lp])
			return;

		while (tmp[lp+1]) {
			g_string_sprintfa (newp, "%s/", tmp[lp]);
			lp++;
		}
		g_free (cur_dir);
		cur_dir = newp->str;
		g_string_free (newp, FALSE);
	} else {
		MsOleStat s;
		MsOleErr  result;

		ptr = get_regexp_name (p, cur_dir, ole);
		if (!ptr)
			return;

		newpath = g_strconcat (cur_dir, ptr, "/", NULL);

		result = ms_ole_stat (&s, ole, newpath, "");
		if (result == MS_OLE_ERR_EXIST) {
			printf ("Storage '%s' not found\n", ptr);
			g_free (newpath);
			return;
		}
		if (result != MS_OLE_ERR_OK) {
			g_warning ("internal error");
			g_free (newpath);
			return;
		}
		if (s.type == MsOleStreamT) {
			printf ("Trying to enter a stream. (%d)\n", s.type);
			g_free (newpath);
			return;
		}

		g_free (cur_dir);
		cur_dir = newpath;
	}
}

static MsOleErr
test_stream_open (MsOleStream ** const stream, MsOle *f,
		  const char *path, const char *fname, char mode)
{
	MsOleErr err;
	char    *name;

	name = get_regexp_name (fname, path, f);
	if (name) {
		err = ms_ole_stream_open (stream, f, path, name, mode);
		g_free (name);
	} else /* Fall back to original */
		err = ms_ole_stream_open (stream, f, fname, name, mode);

	return err;
}


static void
do_dump (MsOle *ole)
{
	char        *ptr;
	MsOleStream *stream;
	guint8      *buffer;
	gchar	    *tname;

	ptr = arg_data [arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}

	tname = g_strdup (ptr);
	if (strcmp(tname, "SummaryInformation") == 0) {
	        printf ("Changing name to prepend 005\n");
		tname = "\05SummaryInformation";
	}
	if (strcmp(tname, "DocumentSummaryInformation") == 0) {
	        printf ("Changing name to prepend 005\n");
		tname = "\05DocumentSummaryInformation";
	}
	
	if (test_stream_open (&stream, ole, cur_dir, tname, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	buffer = g_malloc (stream->size);
	stream->read_copy (stream, buffer, stream->size);
	printf ("Stream : '%s' length 0x%x\n", ptr, stream->size);
	if (buffer)
		ms_ole_dump (buffer, stream->size);
	else
		printf ("Failed read\n");
	ms_ole_stream_close (&stream);
}

/* 
 * This is a massively cut down version ...
 */
typedef struct {
	guint16  opcode;
	guint32  length;        /* NB. can be extended by a continue opcode */
	guint8  *data;
	guint32  streamPos;
	MsOleStream *pos;
} BiffQuery;

static BiffQuery *
ms_biff_query_new (MsOleStream *ptr)
{
	BiffQuery *bq   ;
	if (!ptr)
		return 0;
	bq = g_new0 (BiffQuery, 1);
	bq->opcode = 0;
	bq->length = 0;
	bq->pos    = ptr;
	return bq;
}

static int
ms_biff_query_next (BiffQuery *bq)
{
	guint8  tmp[4];
	int ans=1;

	if (!bq || bq->pos->position >= bq->pos->size)
		return 0;
	if (bq->data)
		g_free (bq->data);

	bq->streamPos = bq->pos->position;
	if (!bq->pos->read_copy (bq->pos, tmp, 4))
		return 0;
	bq->opcode = MS_OLE_GET_GUINT16 (tmp);
	bq->length = MS_OLE_GET_GUINT16 (tmp+2);

	if (bq->length > 0) {
		bq->data = g_new0 (guint8, bq->length);
		if (!bq->pos->read_copy(bq->pos, bq->data, bq->length)) {
			ans = 0;
			g_free (bq->data);
			bq->length = 0;
		}
	}

	if (!bq->length) {
		bq->data = NULL;
		return 1;
	}

	return (ans);
}

static void
do_biff (MsOle *ole)
{
	char *ptr;
	MsOleStream *stream;
	
	ptr = arg_data[arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}
       
	if (test_stream_open (&stream, ole, cur_dir, ptr, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	{
		BiffQuery *q = ms_biff_query_new (stream);
		guint16 last_opcode=0xffff;
		guint32 last_length=0;
		guint32 count=0;
		while (ms_biff_query_next(q)) {
			if (q->opcode == last_opcode &&
			    q->length == last_length)
				count++;
			else {
				if (count>0)
					printf (" x %d\n", count+1);
				else
					printf ("\n");
				count=0;
				printf ("Opcode 0x%3x : %15s, length %d",
					q->opcode, get_biff_opcode_name (q->opcode), q->length);
			}
			last_opcode=q->opcode;
			last_length=q->length;
		}
		printf ("\n");
		ms_ole_stream_close (&stream);
	}
}

static void
do_biff_raw (MsOle *ole)
{
	char *ptr;
	MsOleStream *stream;
	
	ptr = arg_data[arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}
       
	if (test_stream_open (&stream, ole, cur_dir, ptr, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	{
		guint8 data[4], *buffer;
		
		buffer = g_new (guint8, 65550);
		while (stream->read_copy (stream, data, 4)) {
			guint32 len=MS_OLE_GET_GUINT16(data+2);
/*			printf ("0x%4x Opcode 0x%3x : %15s, length 0x%x (=%d)\n", stream->position,
				MS_OLE_GET_GUINT16(data), get_biff_opcode_name (MS_OLE_GET_GUINT16(data)),
				len, len);*/
			printf ("Opcode 0x%3x : %15s, length 0x%x (=%d)\n",
				MS_OLE_GET_GUINT16(data), get_biff_opcode_name (MS_OLE_GET_GUINT16(data)),
				len, len);
			stream->read_copy (stream, buffer, len);
			ms_ole_dump (buffer, len);
			buffer[0]=0;
			buffer[len-1]=0;
		}
		ms_ole_stream_close (&stream);
	}
}

static void
really_get (MsOle *ole, char *from, char *to)
{
	MsOleStream *stream;
	
	if (test_stream_open (&stream, ole, cur_dir, from, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", from);
		return;
	} else {
		guint8 *buffer = g_malloc (stream->size);
		FILE *f = fopen (to, "w");
		stream->read_copy (stream, buffer, stream->size);
		printf ("Stream : '%s' length 0x%x\n", from, stream->size);
		if (f && buffer) {
			fwrite (buffer, 1, stream->size, f);
			fclose (f);
		} else
			printf ("Failed write to '%s'\n", to);
		ms_ole_stream_close (&stream);

	}
}

static void
do_get (MsOle *ole)
{
	char *from, *to;

	from = arg_data[arg_cur++];
	if (!from)
		to = NULL;
	else
		to = arg_data[arg_cur++];
	really_get (ole, from, to);
}

static void
really_put (MsOle *ole, char *from, char *to)
{
	MsOleStream *stream;
	char buffer[8200];

	if (!from || !to) {
		printf ("Null name\n");
		return;
	}

	if (test_stream_open (&stream, ole, cur_dir, to, 'w') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", to);
		return;
	} else {
		FILE *f = fopen (from, "r");
		size_t len;
		int block=0;

		if (!f || !stream) {
			printf ("Failed write\n");
			return;
		}

		stream->lseek (stream, 0, MsOleSeekSet);
	       
		do {
			guint32 lenr = 1+ (int)(8192.0*rand()/(RAND_MAX+1.0));
			len = fread (buffer, 1, lenr, f);
			printf ("Transfering block %d = %d bytes\n", block++, len); 
			stream->write (stream, buffer, len);
		} while (!feof(f) && len>0);

		fclose (f);
		ms_ole_stream_close (&stream);
	}
}

static void
do_summary (MsOle *ole)
{
	MsOleSummary        *si;
	MsOleSummaryPreview  preview;
	gboolean             ok;
	gchar               *txt;
	guint32              num;
	GTimeVal             timeval;

	si = ms_ole_summary_open (ole);
	if (!si) {
		printf ("No summary information\n");
		return;
	}

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_TITLE, &ok);
	if (ok)
		printf ("The title is %s\n", txt);
	else
		printf ("no title found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_SUBJECT, &ok);
	if (ok)
		printf ("The subject is %s\n", txt);
	else
		printf ("no subject found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_AUTHOR, &ok);
	if (ok)
		printf ("The author is %s\n", txt);
	else
		printf ("no author found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_KEYWORDS, &ok);
	if (ok)
		printf ("The keywords are %s\n", txt);
	else
		printf ("no keywords found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_COMMENTS, &ok);
	if (ok)
		printf ("The comments are %s\n", txt);
	else
		printf ("no comments found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_TEMPLATE, &ok);
	if (ok)
		printf ("The template was %s\n", txt);
	else
		printf ("no template found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_LASTAUTHOR, &ok);
	if (ok)
		printf ("The last author was %s\n", txt);
	else
		printf ("no last author found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_REVNUMBER, &ok);
	if (ok)
		printf ("The rev no was %s\n", txt);
	else
		printf ("no rev no found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_APPNAME, &ok);
	if (ok)
		printf ("The app name was %s\n", txt);
	else
		printf ("no app name found\n");
	g_free (txt);

	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_TOTAL_EDITTIME, &ok);
	if (ok)
		printf ("Total edit time is %ld", timeval.tv_sec);
	else
		printf ("no total edit time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_LASTPRINTED, &ok);
	if (ok)
		printf ("Last printed at %s", ctime (&timeval.tv_sec));
	else
		printf ("no last printed time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_CREATED, &ok);
	if (ok)
		printf ("Was created at %s", ctime (&timeval.tv_sec));
	else
		printf ("no creation time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_LASTSAVED, &ok);
	if (ok)
		printf ("Last saved at %s", ctime (&timeval.tv_sec));
	else
		printf ("no last saved time found\n");
	
	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_PAGECOUNT, &ok);
	if (ok)
		printf ("PageCount is %d\n", num);
	else
		printf ("no pagecount found\n");

	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_WORDCOUNT, &ok);
	if (ok)
		printf ("WordCount is %d\n", num);
	else
		printf ("no wordcount found\n");

	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_CHARCOUNT, &ok);
	if (ok)
		printf ("CharCount is %d\n", num);
	else
		printf ("no charcount found\n");
	
	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_SECURITY, &ok);
	if (ok)
		printf ("Security is %d\n", num);
	else
		printf ("no security found\n");

	num = ms_ole_summary_get_short (si, MS_OLE_SUMMARY_CODEPAGE, &ok);
	if (ok)
		printf ("CodePage is %d\n", num);
	else
		printf ("no codepage found\n");

	preview = ms_ole_summary_get_preview (si, MS_OLE_SUMMARY_THUMBNAIL, &ok);
	if (ok) {
		printf ("preview is %d bytes long\n", preview.len);
		ms_ole_summary_preview_destroy (preview);
	} else
		printf ("no preview found\n");

	ms_ole_summary_close (si);
}

static void
do_docsummary (MsOle *ole)
{
	MsOleSummary        *si;
	gboolean             ok;
	gchar               *txt;

	si = ms_ole_docsummary_open (ole);
	if (!si) {
		printf ("No document summary information\n");
		return;
	}

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_CATEGORY, &ok);
	if (ok)
		printf ("The category is %s\n", txt);
	else
		printf ("no category found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_PRESFORMAT, &ok);
	if (ok)
		printf ("The presformat is %s\n", txt);
	else
		printf ("no presformat found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_MANAGER, &ok);
	if (ok)
		printf ("The manager is %s\n", txt);
	else
		printf ("no manager found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_COMPANY, &ok);
	if (ok)
		printf ("The company is %s\n", txt);
	else
		printf ("no company found\n");
	g_free (txt);

	ms_ole_summary_close (si);
}

static void
do_put (MsOle *ole)
{
	char *from, *to;

	from = arg_data[arg_cur++];
	if (!from)
		to = NULL;
	else
		to = arg_data[arg_cur++];

	if (!from || !to) {
		printf ("put <filename> <stream>\n");
		return;
	}

	really_put (ole, from, to);
}

static void
do_copyin (MsOle *ole)
{
	char *from;

	do {
		from = arg_data[arg_cur++];
		if (from)
			really_put (ole, from, from);
	} while (from);
}

static void
do_copyout (MsOle *ole)
{
	char *from;

	do {
		from = arg_data[arg_cur++];
		if (from)
			really_get (ole, from, from);
	} while (from);
}

static void
dump_vba (MsOle *f)
{
	const char	*vbapath = "/_VBA_PROJECT_CUR";
	char		**dir;
	char		*txt;

	int		 i;
	int		 module_count;

	MsOleStream	*s;
	MsOleStat	 st;

	if (ms_ole_stat (&st, f, vbapath, "") != MS_OLE_ERR_OK ||
	    st.type == MsOleStreamT) {
		printf ("No valid VBA found\n");
		return;
	}

	if (test_stream_open (&s, f, vbapath, "PROJECT", 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("No project file... wierd\n");
	} else {
		txt = g_new (guint8, s->size);
		if (!s->read_copy (s, txt, s->size))
			printf ("Failed to read project stream\n");
		else {
			printf ("----------\n");
			printf ("Project file:\n");
			printf ("%s", txt);
			printf ("----------\n");
		}
		ms_ole_stream_close (&s);
		g_free (txt);
	}

	txt = g_strconcat (vbapath, "/VBA", NULL);
	if (ms_ole_directory (&dir, f, txt) != MS_OLE_ERR_OK) {
		printf ("No VBA subdirectory found");
		g_free (txt);
		return;
	}

	module_count = 0;
	for (i = 0; dir [i]; i++) {
		if (test_stream_open (&s, f, txt, dir[i], 'r') !=
		    MS_OLE_ERR_OK)
			printf ("Error opening %s\n", dir [i]);
		else {
			MsOleVba	*vba;

			vba = ms_ole_vba_open (s);
			if (!vba)
				g_warning ("Stream '%s' not VBA", dir [i]);
			else {
				module_count++;

				while (!ms_ole_vba_eof (vba))
					printf ("%c", ms_ole_vba_getc (vba));
			}
			ms_ole_vba_close (vba);

			ms_ole_stream_close (&s);
		}
	}

	if (!module_count)
		printf ("Strange no modules found\n");

	g_free (txt);
}

int
main_original (int argc, char **argv)
{
	MsOle *ole;
	int lp, exit = 0, interact = 0;
	char *buffer = g_new (char, 1024) ;

	if (argc<2)
		syntax_error(0);

	printf ("Ole file '%s'\n", argv[1]);
	if (ms_ole_open_vfs (&ole, argv[1], TRUE, NULL)
	    != MS_OLE_ERR_OK) {
		printf ("Creating new file '%s'\n", argv[1]);
		if (ms_ole_create_vfs (&ole, argv[1], TRUE, NULL)
		    != MS_OLE_ERR_OK)
			syntax_error ("Can't open file or create new one");
	}

	if (argc<=2)
		syntax_error ("Need command or -i");

	if (argc>2 && argv[argc-1][0]=='-'
	    && argv[argc-1][1]=='i') 
		interact=1;
	else {
		char *str=g_strdup(argv[2]) ;
		for (lp=3;lp<argc;lp++)
			str = g_strconcat(str," ",argv[lp],NULL); /* FIXME Mega leak :-) */
		buffer = str; /* and again */
	}

	cur_dir = g_strdup ("/");

	do
	{
		char *ptr;

		if (interact) {
			fprintf (stdout,"> ");
			fflush (stdout);
			fgets (buffer, 1023, stdin);
		}

		arg_data = g_strsplit (g_strchomp (buffer), delim, -1);
		arg_cur  = 0;
		if (!arg_data && interact) continue;
		if (!interact)
			printf ("Command : '%s'\n", arg_data[0]);
		ptr = arg_data[arg_cur++];
		if (!ptr)
			continue;

		if (g_strcasecmp (ptr, "ls") == 0)
			list_files (ole);
		else if (g_strcasecmp (ptr, "cd") == 0)
			enter_dir (ole);
		else if (g_strcasecmp (ptr, "dump") == 0)
			do_dump (ole);
		else if (g_strcasecmp (ptr, "biff") == 0)
			do_biff (ole);
		else if (g_strcasecmp (ptr, "biffraw") == 0)
			do_biff_raw (ole);
		else if (g_strcasecmp (ptr, "get") == 0)
			do_get (ole);
		else if (g_strcasecmp (ptr, "put") == 0)
			do_put (ole);
		else if (g_strcasecmp (ptr, "copyin") == 0)
			do_copyin (ole);
		else if (g_strcasecmp (ptr, "copyout") == 0)
			do_copyout (ole);
		else if (g_strcasecmp (ptr, "summary") == 0)
			do_summary (ole);
		else if (g_strcasecmp (ptr, "docsummary") == 0)
			do_docsummary (ole);
		else if (g_strcasecmp (ptr, "debug") == 0)
			ms_ole_debug (ole, 1);
		else if (g_strcasecmp (ptr, "tree") == 0)
			ms_ole_debug (ole, 2);
		else if (g_strcasecmp (ptr, "vba") == 0)
			dump_vba (ole);
		else if (g_strcasecmp (ptr,"help") == 0 ||
			 g_strcasecmp (ptr,"?") == 0 ||
			 g_strcasecmp (ptr,"info") == 0 ||
			 g_strcasecmp (ptr,"man") == 0)
			list_commands ();
		else if (g_strcasecmp (ptr,"exit") == 0 ||
			 g_strcasecmp (ptr,"quit") == 0 ||
			 g_strcasecmp (ptr,"q") == 0 ||
			 g_strcasecmp (ptr,"bye") == 0)
			exit = 1;
	}
	while (!exit && interact);

	ms_ole_destroy (&ole);
	return 0;
}

//----------------------------------------------------------------------------//

int main_file (int argc, char **argv)
{
	MsOle *ole;
	// int lp, exit = 0, interact = 0;
	// char *buffer = g_new (char, 1024) ;

	printf("\n");

	
	ms_ole_open_vfs (&ole, argv[1], FALSE, NULL);
	do_summary (ole);
	do_docsummary (ole);
	ms_ole_destroy (&ole);
	
	printf("\n");
	return 0;
}

//----------------------------------------------------------------------------//

#define JP_MAX_BUF_LEN 100000
unsigned char jp_buf_buf[JP_MAX_BUF_LEN+1];

MsOle *jp_msOle;

int main_body (int argc, char **argv)
{
	MsOle *msOle;
	MsOleErr msOleErr;
	int fd;

	unsigned char *jp_buf_ptr;
	size_t jp_buf_len;
	
	fd = open(argv[1], O_RDONLY);
	jp_buf_ptr = jp_buf_buf;
	jp_buf_len = read(fd, jp_buf_ptr, JP_MAX_BUF_LEN);
	close(fd);
		
	msOleErr = ms_ole_open_vfs_body (&jp_msOle, argv[1], FALSE, NULL, jp_buf_ptr, jp_buf_len);
	do_summary(jp_msOle);
	do_docsummary(jp_msOle);
	ms_ole_destroy(&jp_msOle);
}

//----------------------------------------------------------------------------//

int main (int argc, char **argv)
{
	printf("\n");
	
	main_file(argc, argv);
	main_body(argc, argv);
	
	printf("\n");
	return 0;
}

//----------------------------------------------------------------------------//

--- Added File version.c in package Packages/Products/DCProject ---
int libole2_major_version = 0;
int libole2_minor_version = 1;
int libole2_micro_version = 7;



--- Added File config.h in package CMF ---
/* config.h.  Generated automatically by configure.  */
#ifndef CONFIG_H_CALLED
#define CONFIG_H_CALLED 1

/* config.h.in.  Generated automatically from configure.in by autoheader.  */

/* Define to empty if the keyword does not work.  */
/* #undef const */

/* Define if you have <sys/wait.h> that is POSIX.1 compatible.  */
/* #undef HAVE_SYS_WAIT_H */

/* Define if you have the wait3 system call.  */
/* #undef HAVE_WAIT3 */

/* Define if you have the waitpid system call.  */
/* #undef HAVE_WAITPID */

/* Define as the return type of signal handlers (int or void).  */
/* #undef RETSIGTYPE */

/* Define if you have the <errno.h> header file.  */
#define HAVE_ERRNO_H 1

/* #undef HAVE_POSIX_SIGNALS */

/* Define to `int' if <sys/types.h> doesn't define.  */
/* #undef pid_t */

/* Define if you have the ANSI C header files.  */
#define STDC_HEADERS 1

/* Define if you have the <fcntl.h> header file.  */
#define HAVE_FCNTL_H 1

/* Define if you have the <sys/file.h> header file.  */
#define HAVE_SYS_FILE_H 1

/* Define if you have the <sys/ioctl.h> header file.  */
#define HAVE_SYS_IOCTL_H 1

/* Define if you have the <unistd.h> header file.  */
#define HAVE_UNISTD_H 1

/* Define if you want zlib to uncompress wmf files */
/* #undef SYSTEM_ZLIB */

/* Define if you have libwmf and want it to convert wmf to gif files */
#define HAVE_WMF 1

/* Define if you have freetype*/
#define HAVE_TTF 1

#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_FCNTL_H
#include <fcntl.h>
#endif

#if defined(HAVE_ERRNO_H)
#include <errno.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>
#include <limits.h>
/* #include <dirent.h> */
#include <stdarg.h>

/*
#include <getopt.h>
*/

#define MATCHED_TYPE 1
/* #undef WORDS_BIGENDIAN */
#define XML_BYTE_ORDER 12

#if defined(__GNUC__) && !defined(WORDS_BIGENDIAN) && defined(MATCHED_TYPE)
#define NO_HOLES
#endif

/* define if you have iconv */
#define USE_ICONV 1

/* define if you have iconv but do not have windows codepage to unicode support */
/* #undef MUST_USE_INTERNAL_ICONV_TABLE */

/* define if you have libpng */
#define HasPNG 1

/* Define if you have the memcpy function.  */
#define HAVE_MEMCPY 1

#ifndef HAVE_MEMCPY
#define memcpy(d, s, n) bcopy ((s), (d), (n))
#endif /* not HAVE_MEMCPY */

#define HAVE_MMAP 1

#endif /* CONFIG_H_CALLED */

--- Added File glibconfig.h in package CMF ---
/* glibconfig.h
 *
 * This is a generated file.  Please modify `configure.in'
 */

#ifndef GLIBCONFIG_H
#define GLIBCONFIG_H

#ifdef __cplusplus
extern "C" {
#endif /* __cplusplus */

#include <limits.h>
#include <float.h>
#define GLIB_HAVE_SYS_POLL_H

#define G_MINFLOAT	FLT_MIN
#define G_MAXFLOAT	FLT_MAX
#define G_MINDOUBLE	DBL_MIN
#define G_MAXDOUBLE	DBL_MAX
#define G_MINSHORT	SHRT_MIN
#define G_MAXSHORT	SHRT_MAX
#define G_MININT	INT_MIN
#define G_MAXINT	INT_MAX
#define G_MINLONG	LONG_MIN
#define G_MAXLONG	LONG_MAX

typedef signed char gint8;
typedef unsigned char guint8;
typedef signed short gint16;
typedef unsigned short guint16;
typedef signed int gint32;
typedef unsigned int guint32;

#if defined (__GNUC__) && (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8))
#  define G_GNUC_EXTENSION __extension__
#else
#  define G_GNUC_EXTENSION
#endif

#define G_HAVE_GINT64 1

G_GNUC_EXTENSION typedef signed long long gint64;
G_GNUC_EXTENSION typedef unsigned long long guint64;

#define G_GINT64_CONSTANT(val)	(G_GNUC_EXTENSION (val##LL))

#define GPOINTER_TO_INT(p)	((gint)   (p))
#define GPOINTER_TO_UINT(p)	((guint)  (p))

#define GINT_TO_POINTER(i)	((gpointer)  (i))
#define GUINT_TO_POINTER(u)	((gpointer)  (u))

#ifdef NeXT /* @#%@! NeXTStep */
# define g_ATEXIT(proc)	(!atexit (proc))
#else
# define g_ATEXIT(proc)	(atexit (proc))
#endif

#define g_memmove(d,s,n) G_STMT_START { memmove ((d), (s), (n)); } G_STMT_END

#define GLIB_MAJOR_VERSION 1
#define GLIB_MINOR_VERSION 2
#define GLIB_MICRO_VERSION 8


#define G_VA_COPY	__va_copy

#ifdef	__cplusplus
#define	G_HAVE_INLINE	1
#else	/* !__cplusplus */
#define G_HAVE_INLINE 1
#define G_HAVE___INLINE 1
#define G_HAVE___INLINE__ 1
#endif	/* !__cplusplus */

#define G_THREADS_ENABLED
#define G_THREADS_IMPL_POSIX
typedef struct _GStaticMutex GStaticMutex;
struct _GStaticMutex
{
  struct _GMutex *runtime_mutex;
  union {
    char   pad[24];
    double dummy_double;
    void  *dummy_pointer;
    long   dummy_long;
  } aligned_pad_u;
};
#define	G_STATIC_MUTEX_INIT	{ NULL, { { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0} } }
#define	g_static_mutex_get_mutex(mutex)   (g_thread_use_default_impl ? ((GMutex*) &((mutex)->aligned_pad_u)) :    g_static_mutex_get_mutex_impl (&((mutex)->runtime_mutex)))

#define GINT16_TO_LE(val)	((gint16) (val))
#define GUINT16_TO_LE(val)	((guint16) (val))
#define GINT16_TO_BE(val)	((gint16) GUINT16_SWAP_LE_BE (val))
#define GUINT16_TO_BE(val)	(GUINT16_SWAP_LE_BE (val))
#define GINT32_TO_LE(val)	((gint32) (val))
#define GUINT32_TO_LE(val)	((guint32) (val))
#define GINT32_TO_BE(val)	((gint32) GUINT32_SWAP_LE_BE (val))
#define GUINT32_TO_BE(val)	(GUINT32_SWAP_LE_BE (val))
#define GINT64_TO_LE(val)	((gint64) (val))
#define GUINT64_TO_LE(val)	((guint64) (val))
#define GINT64_TO_BE(val)	((gint64) GUINT64_SWAP_LE_BE (val))
#define GUINT64_TO_BE(val)	(GUINT64_SWAP_LE_BE (val))
#define GLONG_TO_LE(val)	((glong) GINT32_TO_LE (val))
#define GULONG_TO_LE(val)	((gulong) GUINT32_TO_LE (val))
#define GLONG_TO_BE(val)	((glong) GINT32_TO_BE (val))
#define GULONG_TO_BE(val)	((gulong) GUINT32_TO_BE (val))
#define GINT_TO_LE(val)		((gint) GINT32_TO_LE (val))
#define GUINT_TO_LE(val)	((guint) GUINT32_TO_LE (val))
#define GINT_TO_BE(val)		((gint) GINT32_TO_BE (val))
#define GUINT_TO_BE(val)	((guint) GUINT32_TO_BE (val))
#define G_BYTE_ORDER G_LITTLE_ENDIAN

#define GLIB_SYSDEF_POLLIN =1
#define GLIB_SYSDEF_POLLOUT =4
#define GLIB_SYSDEF_POLLPRI =2
#define GLIB_SYSDEF_POLLERR =8
#define GLIB_SYSDEF_POLLHUP =16
#define GLIB_SYSDEF_POLLNVAL =32


#define G_HAVE_WCHAR_H 1
#define G_HAVE_WCTYPE_H 1


#ifdef __cplusplus
}
#endif /* __cplusplus */

#endif /* GLIBCONFIG_H */

--- Added File libole2.h in package CMF ---
#include "ms-ole.h"
#include "ms-ole-summary.h"

extern int libole2_major_version;
extern int libole2_minor_version;
extern int libole2_micro_version;

#define LIBOLE2_MAJOR_VERSION 0
#define LIBOLE2_MINOR_VERSION 1
#define LIBOLE2_MICRO_VERSION 7

--- Added File ms-ole-summary.c in package CMF ---
/**
 * ms-ole-summary.c: MS Office OLE support
 *
 * Authors:
 *    Michael Meeks (mmeeks@gnu.org)
 *    Frank Chiulli (fc-linux@home.com)
 * From work by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 * Built on work by:
 *    Somar Software's CPPSUM (http://www.somar.com)
 **/

#include <config.h>
#include <glib.h>
#include <stdio.h>

#include <ms-ole.h>
#include <ms-ole-summary.h>


#define SUMMARY_ID(x) ((x) & 0xff)

typedef struct {
	guint32             offset;
	guint32             id;
	MsOlePropertySetID  ps_id;
} item_t;

const guint32	sum_fmtid[4] =  {
				 0xF29F85E0,
		           	 0x10684FF9,
		           	 0x000891AB,
		           	 0xD9B3272B
				};

const guint32	doc_fmtid[4] =  {	
				 0xD5CDD502,
		           	 0x101B2E9C,
		           	 0x00089793,
		           	 0xAEF92C2B
			        };


const guint32	user_fmtid[4] = {			
				 0XD5CDD505,
				 0X101B2E9C,
		           	 0X00089793,
		           	 0XAEF92C2B
				};


static gboolean
read_items (MsOleSummary *si, MsOlePropertySetID ps_id)
{
	gint sect;
	
	for (sect = 0; sect < si->sections->len; sect++) {
		MsOleSummarySection st;
		guint8 data[8];
		gint   i;
		
		st = g_array_index (si->sections, MsOleSummarySection, sect);

		if (st.ps_id != ps_id)
			continue;

		si->s->lseek (si->s, st.offset, MsOleSeekSet);
		if (!si->s->read_copy (si->s, data, 8))
			return FALSE;
		
		st.bytes = MS_OLE_GET_GUINT32 (data);
		st.props = MS_OLE_GET_GUINT32 (data + 4);

		if (st.props == 0)
			continue;
		
		for (i = 0; i < st.props; i++) {
			item_t item;
			if (!si->s->read_copy (si->s, data, 8))
				return FALSE;

			item.id     = MS_OLE_GET_GUINT32 (data);
			item.offset = MS_OLE_GET_GUINT32 (data + 4);
			item.offset = item.offset + st.offset;
			item.ps_id  = ps_id;
			g_array_append_val (si->items, item);
		}
	}
	return TRUE;
}

typedef struct {
	MsOleSummaryPID  id;
	guint32          len;
	guint8          *data;
} write_item_t;


#define PROPERTY_HDR_LEN	8
#define PROPERTY_DESC_LEN	8
static void
write_items (MsOleSummary *si)
{
	MsOlePos cur_pos;
	MsOlePos pos = 48; /* magic offset see: _create_stream */
	guint8   data[PROPERTY_DESC_LEN];
	guint8   fill_data[] = {0, 0, 0, 0};
	guint32  i, num;
	guint32  offset = 0;
	GList   *l;

	/*
	 *  Write out the property descriptors.
	 *  Keep track of the number of properties and number of bytes for the properties.
	 */
	si->s->lseek (si->s, pos + PROPERTY_HDR_LEN, MsOleSeekSet);

	l = si->write_items;
	num = g_list_length (l);
	i = 0;
	offset = PROPERTY_HDR_LEN + num * PROPERTY_DESC_LEN;
	while (l) {
		write_item_t *w = l->data;
		g_return_if_fail (w != NULL);

		/*
		 *  The offset is calculated from the start of the 
		 *  properties header.  The offset must be on a 
		 *  4-byte boundary.  Therefore all data written must be
		 *  in multiples of 4-bytes.
		 */
		MS_OLE_SET_GUINT32 (data + 0, w->id & 0xff);
		MS_OLE_SET_GUINT32 (data + 4, offset);
		si->s->write (si->s, data, PROPERTY_DESC_LEN);

		offset += w->len;
		if ((w->len & 0x3) > 0)
			offset += (4 - (w->len & 0x3));
			
		i++;

		l = g_list_next (l);
	}

	g_return_if_fail (i == num);
	
	/*
	 *  Write out the section header.
	 */
	si->s->lseek (si->s, pos, MsOleSeekSet);
	MS_OLE_SET_GUINT32 (data + 0, offset);
	MS_OLE_SET_GUINT32 (data + 4, i);
	si->s->write (si->s, data, PROPERTY_HDR_LEN);

	/*
	 *  Write out the property values.
	 *  Keep track of the last position written to.
	 */
	cur_pos = pos + PROPERTY_HDR_LEN + num*PROPERTY_DESC_LEN;
	si->s->lseek (si->s, cur_pos, MsOleSeekSet);
	l = si->write_items;
	while (l) {
		write_item_t *w = l->data;
		si->s->write (si->s, w->data, w->len);
		cur_pos += w->len;
		l = g_list_next (l);

		/*
		 * Write out any fill.
		 */
		if ((w->len & 0x3) > 0) {
		        cur_pos += (4 - (w->len & 0x3));
			si->s->write (si->s, fill_data, 4 - (w->len & 0x3));
		}

	}

	/*
	 * Pad it out to a BB file.
	 */
	{
		int     i;
		for (i = cur_pos; i < 0x1000; i+=4)
			si->s->write (si->s, fill_data, 4);
	}

}

/**
 * ms_ole_summary_open_stream:
 * @stream: stream object
 * @psid: Property Set ID, indicates which property set to open
 * 
 * Opens @s as a summary stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * Summary Stream.
 **/
MsOleSummary *
ms_ole_summary_open_stream (MsOleStream *stream,
			    const MsOlePropertySetID psid)
{
	guint8              data[64];
	guint16             byte_order;
	gboolean            panic = FALSE;
	guint32             os_version;
	MsOleSummary       *si;
	gint                i, sections;

	g_return_val_if_fail (stream != NULL, NULL);

	if (!stream->read_copy (stream, data, 28))
		return NULL;

	si                = g_new (MsOleSummary, 1);

	si->s             = stream;
	si->write_items   = NULL;
	si->sections      = NULL;
	si->items         = NULL;
	si->read_mode     = TRUE;

	byte_order        = MS_OLE_GET_GUINT16(data);
	if (byte_order != 0xfffe)
		panic     = TRUE;

	if (MS_OLE_GET_GUINT16 (data + 2) != 0) /* Format */
		panic     = TRUE;

	os_version        = MS_OLE_GET_GUINT32 (data + 4);

	for (i = 0; i < 16; i++)
		si->class_id[i] = data[8 + i];

	sections          = MS_OLE_GET_GUINT32 (data + 24);

	if (panic) {
		ms_ole_summary_close (si);
		return NULL;
	}

	si->sections = g_array_new (FALSE, FALSE, sizeof (MsOleSummarySection));

	for (i = 0; i < sections; i++) {
		MsOleSummarySection sect;
		if (!stream->read_copy (stream, data, 16 + 4)) {
			ms_ole_summary_close (si);
			return NULL;
		}
		
		if (psid == MS_OLE_PS_SUMMARY_INFO) {
			if (MS_OLE_GET_GUINT32 (data +  0) == sum_fmtid[0] &&
			    MS_OLE_GET_GUINT32 (data +  4) == sum_fmtid[1] &&
			    MS_OLE_GET_GUINT32 (data +  8) == sum_fmtid[2] &&
			    MS_OLE_GET_GUINT32 (data + 12) == sum_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_SUMMARY_INFO;
			
			} else {
				ms_ole_summary_close (si);
				return NULL;
			}
			
		} else if (psid == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) {
			if (MS_OLE_GET_GUINT32 (data +  0) == doc_fmtid[0] &&
		            MS_OLE_GET_GUINT32 (data +  4) == doc_fmtid[1] &&
		            MS_OLE_GET_GUINT32 (data +  8) == doc_fmtid[2] &&
		            MS_OLE_GET_GUINT32 (data + 12) == doc_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
			
			} else if (MS_OLE_GET_GUINT32 (data +  0) == user_fmtid[0] &&
		            	   MS_OLE_GET_GUINT32 (data +  4) == user_fmtid[1] &&
				   MS_OLE_GET_GUINT32 (data +  8) == user_fmtid[2] &&
				   MS_OLE_GET_GUINT32 (data + 12) == user_fmtid[3]    ) {
				si->ps_id  = MS_OLE_PS_DOCUMENT_SUMMARY_INFO;
				sect.ps_id = MS_OLE_PS_USER_DEFINED_SUMMARY_INFO;
			
			} else {
				ms_ole_summary_close (si);
				return NULL;
			}
		}

		sect.offset = MS_OLE_GET_GUINT32 (data + 16);
		g_array_append_val (si->sections, sect);
		/* We want to read the offsets of the items here into si->items */
	}

	si->items = g_array_new (FALSE, FALSE, sizeof (item_t));

	for (i = 0; i < sections; i++) {
		MsOleSummarySection st;

		st = g_array_index (si->sections, MsOleSummarySection, i);
		if (!read_items (si, st.ps_id)) {
			// JPP g_warning ("Serious error reading items");
			ms_ole_summary_close (si);
			return NULL;
		}
	}

	return si;
}

/**
 * ms_ole_summary_open:
 * @f: filesystem object.
 * 
 * Opens the SummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * SummaryInformation Stream.
 **/
MsOleSummary *
ms_ole_summary_open (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;
	
	// JPP g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05SummaryInformation", 'r');
	if (result != MS_OLE_ERR_OK || !s)
		return NULL;

	return ms_ole_summary_open_stream (s, MS_OLE_PS_SUMMARY_INFO);
}


/**
 * ms_ole_docsummary_open:
 * @f: filesystem object.
 * 
 * Opens the DocumentSummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to open summary stream or a pointer to the 
 * DocumentSummaryInformation Stream.
 **/
MsOleSummary *
ms_ole_docsummary_open (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;
	
	// JPP g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
	                             "\05DocumentSummaryInformation", 'r');
	if (result != MS_OLE_ERR_OK || !s)
		return NULL;

	return ms_ole_summary_open_stream (s, MS_OLE_PS_DOCUMENT_SUMMARY_INFO);
}


/*
 * Cheat by hard coding magic numbers and chaining on.
 */
/**
 * ms_ole_summary_create_stream:
 * @s: stream object
 * @psid: Property Set ID, indicates which property set to open
 * 
 * Creates @s as a summary stream (@psid determines which one), returns NULL on
 * failure.
 * 
 * Return value: %NULL if unable to create stream, otherwise a pointer to a new
 * summary stream.
 **/
MsOleSummary *
ms_ole_summary_create_stream (MsOleStream *s, const MsOlePropertySetID psid)
{
	guint8        data[78];
	MsOleSummary *si;
	g_return_val_if_fail (s != NULL, NULL);

	MS_OLE_SET_GUINT16 (data +  0, 0xfffe); /* byte order */
	MS_OLE_SET_GUINT16 (data +  2, 0x0000); /* format */
	MS_OLE_SET_GUINT16 (data +  4, 0x0001); /* OS version A */
	MS_OLE_SET_GUINT16 (data +  6, 0x0000); /* OS version B */

	MS_OLE_SET_GUINT32 (data +  8, 0x0000); /* class id */
	MS_OLE_SET_GUINT32 (data + 12, 0x0000);
	MS_OLE_SET_GUINT32 (data + 16, 0x0000);
	MS_OLE_SET_GUINT32 (data + 20, 0x0000);

	if (psid == MS_OLE_PS_SUMMARY_INFO) {
		MS_OLE_SET_GUINT32 (data + 24, 0x0001); /* Sections */

		MS_OLE_SET_GUINT32 (data + 28, sum_fmtid[0]); /* ID */
		MS_OLE_SET_GUINT32 (data + 32, sum_fmtid[1]);
		MS_OLE_SET_GUINT32 (data + 36, sum_fmtid[2]);
		MS_OLE_SET_GUINT32 (data + 40, sum_fmtid[3]);

		MS_OLE_SET_GUINT32 (data + 44, 0x30); /* Section offset = 48 */

		MS_OLE_SET_GUINT32 (data + 48,  0); /* bytes */
		MS_OLE_SET_GUINT32 (data + 52,  0); /* properties */

		s->write (s, data, 56);

	} else if (psid == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) {
		MS_OLE_SET_GUINT32 (data + 24, 0x0001); /* Sections */

		MS_OLE_SET_GUINT32 (data + 28, doc_fmtid[0]); /* ID */
		MS_OLE_SET_GUINT32 (data + 32, doc_fmtid[1]);
		MS_OLE_SET_GUINT32 (data + 36, doc_fmtid[2]);
		MS_OLE_SET_GUINT32 (data + 40, doc_fmtid[3]);

		MS_OLE_SET_GUINT32 (data + 44, 0x30); /* Section offset = 48 */

		MS_OLE_SET_GUINT32 (data + 48,  0); /* bytes */
		MS_OLE_SET_GUINT32 (data + 52,  0); /* properties */

		s->write (s, data, 56);

	}

	s->lseek (s, 0, MsOleSeekSet);

	si = ms_ole_summary_open_stream (s, psid);
	si->read_mode = FALSE;

	return si;
}


/**
 * ms_ole_summary_create:
 * @f: filesystem object.
 * 
 * Create a SummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to create the stream, otherwise a pointer to a
 * new SummaryInformation stream.
 **/
MsOleSummary *
ms_ole_summary_create (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;

	g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05SummaryInformation", 'w');
	if (result != MS_OLE_ERR_OK || !s) {
		printf ("ms_ole_summary_create: Can't open stream for writing\n");
		return NULL;
	}

	return ms_ole_summary_create_stream (s, MS_OLE_PS_SUMMARY_INFO);
}


/**
 * ms_ole_docsummary_create:
 * @f: filesystem object.
 * 
 * Create a DocumentSummaryInformation stream, returns NULL on failure.
 * 
 * Return value: %NULL if unable to create the stream, otherwise a pointer to a
 * new DocumentSummaryInformation stream.
 **/
MsOleSummary *
ms_ole_docsummary_create (MsOle *f)
{
	MsOleStream *s;
	MsOleErr     result;

	g_return_val_if_fail (f != NULL, NULL);

	result = ms_ole_stream_open (&s, f, "/",
				     "\05DocumentSummaryInformation", 'w');
	if (result != MS_OLE_ERR_OK || !s) {
		printf ("ms_ole_docsummary_create: Can't open stream for writing\n");
		return NULL;
	}

	return ms_ole_summary_create_stream (s, MS_OLE_PS_DOCUMENT_SUMMARY_INFO);
}


/* FIXME: without the helpful type */
/**
 * ms_ole_summary_get_properties:
 * @si: summary stream
 * 
 * Returns an array of MsOleSummaryPID.
 * 
 * Return value: an array of property ids in the current summary stream or 
 * %NULL if either the summary stream is non-existent or the summary stream
 * contains no properties.
 **/
GArray *
ms_ole_summary_get_properties (MsOleSummary *si)
{
	GArray *ans;
	gint i;

	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (si->items != NULL, NULL);

	ans = g_array_new (FALSE, FALSE, sizeof (MsOleSummaryPID));
	g_array_set_size  (ans, si->items->len);
	for (i = 0; i < si->items->len; i++)
		g_array_index (ans, MsOleSummaryPID, i) = 
			g_array_index (si->items, item_t, i).id;

	return ans;
}

/**
 * ms_ole_summary_close:
 * @si: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_close (MsOleSummary *si)
{
	g_return_if_fail (si != NULL);
	g_return_if_fail (si->s != NULL);

	if (!si->read_mode)
		write_items (si);
	
	if (si->sections)
		g_array_free (si->sections, TRUE);
	si->sections = NULL;

	if (si->items)
		g_array_free (si->items, TRUE);
	si->items = NULL;

	if (si->s)
		ms_ole_stream_close (&si->s);
	si->s = NULL;

	g_free (si);
}


/*
 *                        Record handling code
 */
#define TYPE_SHORT	0x02		/*   2, VT_I2,		2-byte signed integer  */
#define TYPE_LONG       0x03		/*   3, VT_I4,		4-byte signed integer  */
#define TYPE_BOOLEAN	0x0b		/*  11, VT_BOOL,	Boolean value  */
#define TYPE_STRING     0x1e		/*  30, VT_LPSTR,	Pointer to null terminated ANSI string */
#define TYPE_TIME       0x40		/*  64, VT_FILETIME,	64-bit FILETIME structure  */
#define TYPE_PREVIEW    0x47		/*  71, VT_CF,		Pointer to a CLIPDATA structure  */

/* Seeks to the correct place, and returns a handle or NULL on failure */
static item_t *
seek_to_record (MsOleSummary *si, MsOleSummaryPID id)
{
	gint i;
	g_return_val_if_fail (si->items, FALSE);

	/* These should / could be sorted for speed */
	for (i = 0; i < si->items->len; i++) {
		item_t item = g_array_index (si->items, item_t, i);
		if (item.id == SUMMARY_ID(id)) {
			gboolean is_summary, is_doc_summary;

			is_summary     = ((si->ps_id == MS_OLE_PS_SUMMARY_INFO) && 
					  (item.ps_id == MS_OLE_PS_SUMMARY_INFO));
			is_doc_summary = ((si->ps_id == MS_OLE_PS_DOCUMENT_SUMMARY_INFO) && 
					  (item.ps_id == MS_OLE_PS_DOCUMENT_SUMMARY_INFO));
			if (is_summary || is_doc_summary) {
				si->s->lseek (si->s, item.offset, MsOleSeekSet);
				return &g_array_index (si->items, item_t, i);
			}
		}
	}
	return NULL;
}

/**
 * ms_ole_summary_get_string:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * Note: Ensure that you free returned value after use.
 * 
 * Return value: FIXME
 **/
char *
ms_ole_summary_get_string (MsOleSummary *si, MsOleSummaryPID id,
			   gboolean *available)
{
	guint8   data[8];
	guint32  type, len;
	gchar   *ans;
	item_t *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (si->read_mode, NULL);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_STRING, NULL);

	if (!(item = seek_to_record (si, id)))
		return NULL;

	if (!si->s->read_copy (si->s, data, 8))
		return NULL;

	type = MS_OLE_GET_GUINT32 (data);
	len  = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_STRING) { /* Very odd */
		// JPP g_warning ("Summary string type mismatch");
		return NULL;
	}

	ans = g_new (gchar, len + 1);
	
	if (!si->s->read_copy (si->s, ans, len)) {
		g_free (ans);
		return NULL;
	}

	ans[len] = '\0';

	*available = TRUE;
	return ans;
}

/**
 * ms_ole_summary_get_short:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
guint16
ms_ole_summary_get_short (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8   data[8];
	guint32  type;
	guint32  value;
	item_t  *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_SHORT, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32 (data);
	value = MS_OLE_GET_GUINT16 (data + 4);

	if (type != TYPE_SHORT) { /* Very odd */
		// JPP g_warning ("Summary short type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}

/**
 * ms_ole_summary_get_boolean:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
gboolean
ms_ole_summary_get_boolean (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean *available)
{
	guint8    data[8];
	guint32   type;
	gboolean  value;
	item_t   *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_BOOLEAN, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32  (data);
	value = MS_OLE_GET_GUINT16 (data + 4);

	if (type != TYPE_BOOLEAN) { /* Very odd */
		// JPP g_warning ("Summary boolean type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}

/**
 * ms_ole_summary_get_long:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
guint32
ms_ole_summary_get_long (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8  data[8];
	guint32 type, value;
	item_t *item;

	g_return_val_if_fail (available != NULL, 0);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, 0);
	g_return_val_if_fail (si->read_mode, 0);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_LONG, 0);

	if (!(item = seek_to_record (si, id)))
		return 0;

	if (!si->s->read_copy (si->s, data, 8))
		return 0;

	type  = MS_OLE_GET_GUINT32 (data);
	value = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_LONG) { /* Very odd */
		// JPP g_warning ("Summary long type mismatch");
		return 0;
	}

	*available = TRUE;
	return value;
}


/*
 *  filetime_to_unixtime
 *
 *  Convert a FILETIME format to unixtime
 *  FILETIME is the number of 100ns units since January 1, 1601.
 *  unixtime is the number of seconds since January 1, 1970.
 *
 *  The difference in 100ns units between the two dates is:
 *	116,444,736,000,000,000  (TIMEDIF)
 *  (I'll let you do the math)
 *  If we divide this into pieces,
 *    high 32-bits = 27111902 or  TIMEDIF / 16^8
 *    mid  16-bits =    54590 or (TIMEDIF - (high 32-bits * 16^8)) / 16^4
 *    low  16-bits =    32768 or (TIMEDIF - (high 32-bits * 16^8) - (mid 16-bits * 16^4)
 *
 *  where all math is integer.
 *
 *  Adapted from work in 'wv' by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 */
#define HIGH32_DELTA	27111902
#define MID16_DELTA	   54590
#define LOW16_DELTA        32768

/**
 * filetime_to_unixtime:
 * @low_time: FIXME
 * @high_time: FIXME
 * 
 * Converts a FILETIME format to unixtime. FILETIME is the number of 100ns units
 * since January 1, 1601. unixtime is the number of seconds since January 1,
 * 1970.
 * 
 * Return value: FIXME
 **/
glong filetime_to_unixtime (guint32 low_time, guint32 high_time);
glong
filetime_to_unixtime (guint32 low_time, guint32 high_time)
{
	guint32		 low16;		/* 16 bit, low    bits */
	guint32		 mid16;		/* 16 bit, medium bits */
	guint32		 hi32;		/* 32 bit, high   bits */
	unsigned int	 carry;		/* carry bit for subtraction */
	int		 negative;	/* whether a represents a negative value */

	/* Copy the time values to hi32/mid16/low16 */
	hi32  =  high_time;
	mid16 = low_time >> 16;
	low16 = low_time &  0xffff;

	/* Subtract the time difference */
	if (low16 >= LOW16_DELTA           )
		low16 -=             LOW16_DELTA        , carry = 0;
	else
		low16 += (1 << 16) - LOW16_DELTA        , carry = 1;

	if (mid16 >= MID16_DELTA    + carry)
		mid16 -=             MID16_DELTA + carry, carry = 0;
	else
		mid16 += (1 << 16) - MID16_DELTA - carry, carry = 1;

	hi32 -= HIGH32_DELTA + carry;

	/* If a is negative, replace a by (-1-a) */
	negative = (hi32 >= ((guint32)1) << 31);
	if (negative) {
		/* Set a to -a - 1 (a is hi32/mid16/low16) */
		low16 = 0xffff - low16;
		mid16 = 0xffff - mid16;
		hi32 = ~hi32;
	}

	/*
	 *  Divide a by 10000000 (a = hi32/mid16/low16), put the rest into r.
         * Split the divisor into 10000 * 1000 which are both less than 0xffff.
	 */
	mid16 += (hi32 % 10000) << 16;
	hi32  /=       10000;
	low16 += (mid16 % 10000) << 16;
	mid16 /=       10000;
	low16 /=       10000;

	mid16 += (hi32 % 1000) << 16;
	hi32  /=       1000;
	low16 += (mid16 % 1000) << 16;
	mid16 /=       1000;
	low16 /=       1000;

	/* If a was negative, replace a by (-1-a) and r by (9999999 - r) */
	if (negative) {
		/* Set a to -a - 1 (a is hi32/mid16/low16) */
		low16 = 0xffff - low16;
		mid16 = 0xffff - mid16;
		hi32 = ~hi32;
	}

	/*  Do not replace this by << 32, it gives a compiler warning and 
	 *  it does not work
	 */
	return ((((glong)hi32) << 16) << 16) + (mid16 << 16) + low16;

}


/**
 * unixtime_to_filetime:
 * @unix_time: FIXME
 * @time_high: FIXME
 * @time_low: FIXME
 * 
 * Converts a unixtime format to FILETIME. FILETIME is the number of 100ns units
 * since January 1, 1601. unixtime is the number of seconds since January 1,
 * 1970.
 **/
void unixtime_to_filetime (time_t unix_time, unsigned int *time_high,
			   unsigned int *time_low);
void
unixtime_to_filetime (time_t unix_time, unsigned int *time_high, unsigned int *time_low)
{
	unsigned int	 low_16;
	unsigned int	 mid_16;
	unsigned int	 high32;
	unsigned int	 carry;
	
	/*
	 *  First split unix_time up.
	 */
	high32 = (unix_time >> 16) >> 16;
	mid_16 =  unix_time >> 16;
	low_16 =  unix_time  & 0xffff;
	
	/*
	 *  Convert seconds to 100 ns units by multipling by 10,000,000.
	 *  Do this in two steps, 10,000 and 1,000.
	 */
	low_16 *= 10000;
	carry   = (low_16) >> 16;
	low_16  = low_16 & 0xffff;
	
	mid_16 *= 10000;
	mid_16 += carry;
	carry   = (mid_16 >> 16);
	mid_16  = mid_16 & 0xffff;
	
	high32 *= 10000;
	high32 += carry;
	
	
	low_16 *= 1000;
	carry   = (low_16) >> 16;
	low_16  = low_16 & 0xffff;
	
	mid_16 *= 1000;
	mid_16 += carry;
	carry   = (mid_16 >> 16);
	mid_16  = mid_16 & 0xffff;
	
	high32 *= 1000;
	high32 += carry;
	
	/*
	 *  Now add in the time difference.
	 */
	low_16 += LOW16_DELTA;
	mid_16 += (low_16 >> 16);
	low_16  =  low_16  & 0xffff;
	
	mid_16 += MID16_DELTA;
	high32 += (mid_16 >> 16);
	mid_16  =  mid_16  & 0xffff;
	
	high32 += HIGH32_DELTA;
	
	*time_high = high32;
	*time_low  = (mid_16 << 16) + low_16;
	
	return;
	
}


/**
 * ms_ole_summary_get_time:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
GTimeVal
ms_ole_summary_get_time (MsOleSummary *si, MsOleSummaryPID id,
			 gboolean *available)
{
	guint8   data[12];
	guint32  type;
	guint32  low_time;
	guint32  high_time;
	item_t  *item;
	GTimeVal time;

	time.tv_sec  = 0;   /* Magic numbers */
	time.tv_usec = 0;
/*	g_date_set_dmy (&time.date, 18, 6, 1977); */

	g_return_val_if_fail (available != NULL, time);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, time);
	g_return_val_if_fail (si->read_mode, time);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_TIME, time);

	if (!(item = seek_to_record (si, id)))
		return time;

	if (!si->s->read_copy (si->s, data, 12))
		return time;

	type      = MS_OLE_GET_GUINT32 (data);
	low_time  = MS_OLE_GET_GUINT32 (data + 4);
	high_time = MS_OLE_GET_GUINT32 (data + 8);

	if (type != TYPE_TIME) { /* Very odd */
		// JPP g_warning ("Summary time type mismatch");
		return time;
	}

	time.tv_sec = filetime_to_unixtime (low_time, high_time);
	
	*available = TRUE;
	return time;
}

/**
 * ms_ole_summary_preview_destroy:
 * @d: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_preview_destroy (MsOleSummaryPreview d)
{
	if (d.data)
		g_free (d.data);
	d.data = NULL;
}

/**
 * ms_ole_summary_get_preview:
 * @si: FIXME
 * @id: FIXME
 * @available: FIXME
 * 
 * FIXME
 * 
 * Return value: FIXME
 **/
MsOleSummaryPreview
ms_ole_summary_get_preview (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean *available)
{
	guint8  data[8];
	guint32 type;
	MsOleSummaryPreview ans;
	item_t *item;

	ans.len  = 0;
	ans.data = NULL;

	g_return_val_if_fail (available != NULL, ans);
	*available = FALSE;
	g_return_val_if_fail (si != NULL, ans);
	g_return_val_if_fail (si->read_mode, ans);
	g_return_val_if_fail (MS_OLE_SUMMARY_TYPE (id) ==
			      MS_OLE_SUMMARY_TYPE_OTHER, ans);

	if (!(item = seek_to_record (si, id)))
		return ans;

	if (!si->s->read_copy (si->s, data, 8))
		return ans;

	type     = MS_OLE_GET_GUINT32 (data);
	ans.len  = MS_OLE_GET_GUINT32 (data + 4);

	if (type != TYPE_PREVIEW) { /* Very odd */
		// JPP g_warning ("Summary wmf type mismatch");
		return ans;
	}

	ans.data = g_new (guint8, ans.len + 1);
	
	if (!si->s->read_copy (si->s, ans.data, ans.len)) {
		g_free (ans.data);
		return ans;
	}

	*available = TRUE;
	return ans;
}

static write_item_t *
write_item_t_new (MsOleSummary *si, MsOleSummaryPID id)
{
	write_item_t *w = g_new (write_item_t, 1);

	g_return_val_if_fail (si != NULL, NULL);
	g_return_val_if_fail (!si->read_mode, NULL);

	w->id           = id;
	w->len          = 0;
	w->data         = NULL;
	si->write_items = g_list_append (si->write_items, w);

	return w;
}

/**
 * ms_ole_summary_set_preview:
 * @si: FIXME
 * @id: FIXME
 * @preview: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_preview (MsOleSummary *si, MsOleSummaryPID id,
			    const MsOleSummaryPreview *preview)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);
	g_return_if_fail (preview != NULL);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, preview->len + 8);

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_PREVIEW);
	MS_OLE_SET_GUINT32 (w->data + 4, preview->len);

	memcpy (w->data + 8, preview->data, preview->len);
	
	w->len = preview->len + 8;
}

/**
 * ms_ole_summary_set_time:
 * @si: FIXME
 * @id: FIXME
 * @time: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_time (MsOleSummary *si, MsOleSummaryPID id,
			 GTimeVal time)
{
	unsigned int	 time_high;
	unsigned int	 time_low;
	write_item_t	*w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 12);
	w->len  = 12;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_TIME);
	
        unixtime_to_filetime ((time_t)time.tv_sec, &time_high, &time_low);
	
	MS_OLE_SET_GUINT32 (w->data + 4, time_low);
	MS_OLE_SET_GUINT32 (w->data + 8, time_high);
}

/**
 * ms_ole_summary_set_boolean:
 * @si: FIXME
 * @id: FIXME
 * @bool: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_boolean (MsOleSummary *si, MsOleSummaryPID id,
			    gboolean value)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 6;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_BOOLEAN);
	MS_OLE_SET_GUINT16 (w->data + 4, value);
}



/**
 * ms_ole_summary_set_short:
 * @si: FIXME
 * @id: FIXME
 * @i: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_short (MsOleSummary *si, MsOleSummaryPID id,
			  guint16 i)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 6;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_SHORT);
	MS_OLE_SET_GUINT16 (w->data + 4, i);
}

/**
 * ms_ole_summary_set_long:
 * @si: FIXME
 * @id: FIXME
 * @i: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_long (MsOleSummary *si, MsOleSummaryPID id,
			 guint32 i)
{
	write_item_t *w;

	g_return_if_fail (si != NULL);
	g_return_if_fail (!si->read_mode);

	w = write_item_t_new (si, id);

	w->data = g_new (guint8, 8);
	w->len  = 8;

	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_LONG);
	MS_OLE_SET_GUINT32 (w->data + 4, i);
}

/**
 * ms_ole_summary_set_string:
 * @si: FIXME
 * @id: FIXME
 * @str: FIXME
 * 
 * FIXME
 **/
void
ms_ole_summary_set_string (MsOleSummary *si, MsOleSummaryPID id,
			   const gchar *str)
{
	write_item_t *w;
	guint32 len;

	g_return_if_fail (si != NULL);
	g_return_if_fail (str != NULL);
	g_return_if_fail (!si->read_mode);

	w       = write_item_t_new (si, id);
	len     = strlen (str) + 1;
	w->len  = len + 8;
	w->data = g_new (guint8, len + 8);
	
	MS_OLE_SET_GUINT32 (w->data + 0, TYPE_STRING);
	MS_OLE_SET_GUINT32 (w->data + 4, len);

	memcpy (w->data + 8, str, len);
}

--- Added File ms-ole-summary.h in package CMF ---
/**
 * ms-ole-summary.h: MS Office OLE support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 * From work by:
 *    Caolan McNamara (Caolan.McNamara@ul.ie)
 * Built on work by:
 *    Somar Software's CPPSUM (http://www.somar.com)
 **/

#ifndef MS_OLE_SUMMARY_H
#define MS_OLE_SUMMARY_H

#include <time.h>
#include <ms-ole.h>

/*
 * MS Ole Property Set IDs
 * The SummaryInformation stream contains the SummaryInformation property set.
 * The DocumentSummaryInformation stream contains both the
 * DocumentSummaryInformation and the UserDefined property sets as sections.
 */
typedef enum {
	MS_OLE_PS_SUMMARY_INFO,
	MS_OLE_PS_DOCUMENT_SUMMARY_INFO,
	MS_OLE_PS_USER_DEFINED_SUMMARY_INFO
} MsOlePropertySetID;

typedef struct {
	guint8			class_id[16];
	GArray *		sections;
	GArray *		items;
	GList *			write_items;
	gboolean		read_mode;
	MsOleStream *		s;
	MsOlePropertySetID	ps_id;
} MsOleSummary;

/* Could store the FID, but why bother ? */
typedef struct {
	guint32			offset;
	guint32			props;
	guint32			bytes;
	MsOlePropertySetID	ps_id;
} MsOleSummarySection;

MsOleSummary *ms_ole_summary_open		(MsOle *f);
MsOleSummary *ms_ole_docsummary_open		(MsOle *f);
MsOleSummary *ms_ole_summary_open_stream	(MsOleStream *stream,
						 const MsOlePropertySetID psid);
MsOleSummary *ms_ole_summary_create		(MsOle *f);
MsOleSummary *ms_ole_docsummary_create		(MsOle *f);
MsOleSummary *ms_ole_summary_create_stream	(MsOleStream *s,
						 const MsOlePropertySetID psid);
GArray       *ms_ole_summary_get_properties	(MsOleSummary *si);
void	      ms_ole_summary_close		(MsOleSummary *si);


/*
 * Can be used to interrogate a summary item as to its type
 */
typedef enum {
	MS_OLE_SUMMARY_TYPE_STRING  = 0x10,
	MS_OLE_SUMMARY_TYPE_TIME    = 0x20,
	MS_OLE_SUMMARY_TYPE_LONG    = 0x30,
	MS_OLE_SUMMARY_TYPE_SHORT   = 0x40,
	MS_OLE_SUMMARY_TYPE_BOOLEAN = 0x50,
	MS_OLE_SUMMARY_TYPE_OTHER   = 0x60
} MsOleSummaryType;

#define MS_OLE_SUMMARY_TYPE(x) ((MsOleSummaryType)((x)>>8))

/* FIXME MS_OLE_SUMMARY_THUMBNAIL is Preview, no Security, isn't it? */
/*
 *  The MS byte specifies the type, the LS byte is the
 * 'standard' MS PID.
 */
typedef enum {
/* SummaryInformation Stream Properties */
/* String properties */
	MS_OLE_SUMMARY_TITLE          = 0x1002,
	MS_OLE_SUMMARY_SUBJECT        = 0x1003,
	MS_OLE_SUMMARY_AUTHOR         = 0x1004,
	MS_OLE_SUMMARY_KEYWORDS       = 0x1005,
	MS_OLE_SUMMARY_COMMENTS       = 0x1006,
	MS_OLE_SUMMARY_TEMPLATE       = 0x1007,
	MS_OLE_SUMMARY_LASTAUTHOR     = 0x1008,
	MS_OLE_SUMMARY_REVNUMBER      = 0x1009,
	MS_OLE_SUMMARY_APPNAME        = 0x1012,
	
/* Time properties */
	MS_OLE_SUMMARY_TOTAL_EDITTIME = 0x200A,
	MS_OLE_SUMMARY_LASTPRINTED    = 0x200B,
	MS_OLE_SUMMARY_CREATED        = 0x200C,
	MS_OLE_SUMMARY_LASTSAVED      = 0x200D,
	
/* Long integer properties */
	MS_OLE_SUMMARY_PAGECOUNT      = 0x300E,
	MS_OLE_SUMMARY_WORDCOUNT      = 0x300F,
	MS_OLE_SUMMARY_CHARCOUNT      = 0x3010,
	MS_OLE_SUMMARY_SECURITY       = 0x3013,

/* Short integer properties */
	MS_OLE_SUMMARY_CODEPAGE       = 0x4001,

/* Security */	
	MS_OLE_SUMMARY_THUMBNAIL      = 0x6011,


/* DocumentSummaryInformation Properties */
/* String properties */
	MS_OLE_SUMMARY_CATEGORY	      = 0x1002,
	MS_OLE_SUMMARY_PRESFORMAT     = 0x1003,
	MS_OLE_SUMMARY_MANAGER        = 0x100E,
	MS_OLE_SUMMARY_COMPANY        = 0x100F,

/* Long integer properties */
	MS_OLE_SUMMARY_BYTECOUNT      = 0x3004,
	MS_OLE_SUMMARY_LINECOUNT      = 0x3005,
	MS_OLE_SUMMARY_PARCOUNT       = 0x3006,
	MS_OLE_SUMMARY_SLIDECOUNT     = 0x3007,
	MS_OLE_SUMMARY_NOTECOUNT      = 0x3008,
	MS_OLE_SUMMARY_HIDDENCOUNT    = 0x3009,
	MS_OLE_SUMMARY_MMCLIPCOUNT    = 0X300A,

/* Boolean properties */
	MS_OLE_SUMMARY_SCALE          = 0x500B,
	MS_OLE_SUMMARY_LINKSDIRTY     = 0x5010
} MsOleSummaryPID;


/* bit masks for security long integer */
#define MsOleSummaryAllSecurityFlagsEqNone        0x00
#define MsOleSummarySecurityPassworded            0x01
#define MsOleSummarySecurityRORecommended         0x02
#define MsOleSummarySecurityRO                    0x04
#define MsOleSummarySecurityLockedForAnnotations  0x08

typedef struct {
	GTimeVal time;
	GDate    date;
} MsOleSummaryTime;

typedef struct {
	guint32 len;
	guint8 *data;
} MsOleSummaryPreview;

gchar *			ms_ole_summary_get_string	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
gboolean		ms_ole_summary_get_boolean	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
guint16			ms_ole_summary_get_short	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
guint32			ms_ole_summary_get_long		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
GTimeVal		ms_ole_summary_get_time		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
MsOleSummaryPreview	ms_ole_summary_get_preview	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean *available);
void			ms_ole_summary_preview_destroy	(MsOleSummaryPreview d);

/* FIXME The next comment isn't true, is it?
   Return TRUE if write is successful */
void			ms_ole_summary_set_string	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 const gchar *str);
void			ms_ole_summary_set_boolean	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 gboolean value);
void			ms_ole_summary_set_short	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 guint16 i);
void			ms_ole_summary_set_long		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 guint32 i);
void			ms_ole_summary_set_time		(MsOleSummary *si,
							 MsOleSummaryPID id,
							 GTimeVal time);
void			ms_ole_summary_set_preview	(MsOleSummary *si,
							 MsOleSummaryPID id,
							 const
							 MsOleSummaryPreview *
							 preview);

#endif	/* MS_OLE_SUMMARY_H */


--- Added File ms-ole-vba.c in package CMF ---
/**
 * ms-ole-vba.c: MS Office VBA support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 *
 * Copyright 2000 Helix Code, Inc.
 **/

#include <config.h>
#include <stdio.h>

#include <ms-ole-vba.h>

#undef VBA_DEBUG


struct _MsOleVba {
	MsOleStream *s;
	GArray      *text;
	int          pos;
};

inline gboolean
ms_ole_vba_eof (MsOleVba *vba)
{
	return !vba || (vba->pos >= vba->text->len - 1);
}

char
ms_ole_vba_getc (MsOleVba *vba)
{
	g_assert (!ms_ole_vba_eof (vba));

	return g_array_index (vba->text, guint8, vba->pos++);
}

char
ms_ole_vba_peek (MsOleVba *vba)
{
	g_assert (!ms_ole_vba_eof (vba));

	return g_array_index (vba->text, guint8, vba->pos);
}

#if VBA_DEBUG > 1
static void
print_bin (guint16 dt)
{
	int i;
	
	printf ("|");
	for (i = 15; i >= 0; i--) {
		if (dt & (1 << i))
			printf ("1");
		else
			printf ("0");
		if (i == 8)
			printf ("|");
	}
	printf ("|");
}
#endif


/**
 * decompress_vba
 *   @vba	Place to store the uncompressed VBA
 *   @data	Pointer to start of compressed VBA
 *   @eos	lwa+1 of stream
 *
 * Purpose: lzw, arc like compression.
 *
 * Internal function.
 **/
static void
decompress_vba (MsOleVba *vba, guint8 *data, guint8 *eos)
{
#define BUF_SIZE    6144 /* a bottleneck */

	guint8	 buffer[BUF_SIZE];
	guint8  *ptr;
	guint8	*sptr;

	guint32	 len;
	guint32  pos;

	GArray  *ans = g_array_new (FALSE, FALSE, 1);


	vba->text = ans;
	vba->pos  = 0;

	len = MS_OLE_GET_GUINT16 (data + 1);

#if VBA_DEBUG > 0
	printf ("Length 0x%x\n", len);
#endif

	len  = (len & ~0xb000) + 1;
	ptr  = data + 3;
	sptr = ptr;
	pos  = 0;

	while (ptr < eos) {
#if VBA_DEBUG > 0
		printf ("My compressed stream (addr=%#x, len = %#x (%d)):\n", 
		        ptr - data, len, len);
		ms_ole_dump (ptr, len);
#endif

		while ((ptr < sptr + len) && (ptr < eos)) {
			int	 shift;

			guint8	 flag_byte = *ptr++;

			/*
			 * The first byte is a flag byte.  Each bit in this byte
			 * determines what the next byte is.  If the bit is zero,
			 * the next byte is a character.  Otherwise the  next two
			 * bytes contain the number of characters to copy from the
			 * umcompresed buffer and where to copy them from (offset,
			 * length).
			 */
			for (shift = 0x01; shift < 0x100; shift = shift << 1) {
				if (ptr >= sptr + len)
					break;

				if (pos == BUF_SIZE) {
#if VBA_DEBUG > 0
					printf ("\nSomething extremely odd"
						" happens after %d bytes 0x%x\n\n",
						BUF_SIZE, MS_OLE_GET_GUINT16 (ptr));
					ms_ole_dump (ptr, len - (ptr - data));
#endif
					ptr       += 2;
					flag_byte  = *ptr++;
					pos        = 0;
					shift      = 0x01;
				}

				if (flag_byte & shift) {
					int	 i;
					int	 back;
					int	 clen;
					int	 shft;

					guint16	 dt = MS_OLE_GET_GUINT16 (ptr);

					if (pos <= 16)
						shft = 12;

					else if (pos <= 32)
						shft = 11;

					else if (pos <= 64)
						shft = 10;

					else if (pos <= 128)
						shft = 9;

					else if (pos <= 256)
						shft = 8;

					else if (pos <= 512)
						shft = 7;

					else if (pos <= 1024)
						shft = 6;

					else if (pos <= 2048)
						shft = 5;

					else
						shft = 4;

					back = (dt >> shft) + 1;
					clen = 0;

					for (i = 0; i < shft; i++)
						clen |= dt & (0x1 << i);
					clen += 3;

#if VBA_DEBUG > 1
					printf ("|match 0x%x (%d,%d) >> %d = %d, %d| pos = %d |\n",
						dt, (dt>>8), (dt&0xff), shft, back, clen, pos);
 					/* Perhaps dt & SHIFT = dist. to end of run */
					print_bin (dt);
					printf ("\n");
#endif				
					for (i = 0; i < clen; i++) {
						guint8	 c;

						guint32	 srcpos = (BUF_SIZE + (pos%BUF_SIZE)) - 
								   back;
						
						if (srcpos >= BUF_SIZE)
							srcpos-= BUF_SIZE;

						g_assert (srcpos >= 0);
						g_assert (srcpos < BUF_SIZE);

						c = buffer [srcpos];
						buffer [pos++ % BUF_SIZE] = c;
						g_array_append_val (ans, c);
#if VBA_DEBUG > 0
						printf ("%c", c);
#endif
					}
					ptr += 2;

				} else {
					buffer [pos++ % BUF_SIZE] = *ptr;
					g_array_append_val (ans, *ptr);
#if VBA_DEBUG > 0
					printf ("%c", *ptr);
#endif
					ptr++;
				}

				if ((ptr >= sptr + len) || (ptr >= eos)) {
					if ((ptr >= sptr + len) && (ptr < eos)) {
#if VBA_DEBUG > 0
						printf ("Reseting ptr.\n");
						printf ("ptr was %#x\n",
						        ptr - sptr);
						printf ("ptr is  %#x\n",
						        len);
#endif
						ptr = sptr + len;
					}
					break;
				}
			}  /* for (shift = 0x01; shift < 0x100; shift = shift << 1) */

		}  /* while ((ptr < sptr + len) && (ptr < eos)) */

#if VBA_DEBUG > 0
		printf ("ptr = %#X\n", ptr-sptr);
#endif
		if ((ptr + 3) < eos) {
			len  = MS_OLE_GET_GUINT16 (ptr);
			len  = (len & ~0xb000) + 1;
			ptr += 2;
			sptr = ptr;
			pos  = 0;
		}

	}  /* while (ptr < eos) */
	
	{
		char c;

		c = '\n';
		g_array_append_val (ans, c);

		c = '\0';
		g_array_append_val (ans, c);
	}
}

static guint8 *
seek_sig (guint8 *data, int len)
{
	int i;
	guint8 vba_sig[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1, 0x1 };

	for (i = 0; i < len; i++) {
		guint8 *p = data;
		int j;

		for (j = 0; j < sizeof (vba_sig); j++) {
			if (*p++ != vba_sig [j])
				break;
		}
		if (j == sizeof (vba_sig))
			return p;

		data++;
	}

	return NULL;
}

static guint8 *
find_compressed_vba (guint8 *data, MsOlePos len)
{
	guint8	*sig;
	guint32  offset;
	guint32  offpos;
		
	if (!(sig = seek_sig (data, len))) {
		g_warning ("No VBA kludge signature");
		return NULL;
	}

	offpos = MS_OLE_GET_GUINT32 (sig) + 0xd0 - 0x6b - 0x8;

#if VBA_DEBUG > 0
	printf ("Offpos : 0x%x -> \n", offpos);
#endif

	offset = MS_OLE_GET_GUINT32 (sig + offpos);

	if (len < offset + 3) {
		g_warning ("Too small for offset 0x%x\n", offset);
		return NULL;
	}

#if VBA_DEBUG > 0
	printf ("Offset is 0x%x\n", offset);
#endif

	return data + offset;
}


/**
 * ms_ole_vba_open:
 * @s: the stream pointer.
 * 
 * Attempt to open a stream as a VBA stream, and commence
 * decompression of it.
 * 
 * Return value: NULL if not a VBA stream or fails.
 **/
MsOleVba *
ms_ole_vba_open (MsOleStream *s)
{
	const guint8	 gid [16] = { 0x1,  0x16, 0x1,  0x0,
				      0x6,  0xb6, 0x0,  0xff,
				      0xff, 0x1,  0x1,  0x0,
				      0x0,  0x0,  0x0,  0xff };
	
	int		 i;
	int		 j;
	int		 len;
	
	guint8      *data, *vba_data;
	guint8       sig [16];
	
	MsOleVba	*vba;


	g_return_val_if_fail (s != NULL, NULL);

	if (s->size < 16)
		return NULL;

	s->lseek     (s, 0, MsOleSeekSet);
	s->read_copy (s, sig, 16);

	for (i = 0; i < 16; i++)
		if (sig [i] != gid [i]) {
			/*
			 * Version ??
			*/
			if (i == 4)
			  if (sig [i] == 0x4 )
			  	continue;
			return NULL;
		}

	data = g_new (guint8, s->size);

	s->lseek (s, 0, MsOleSeekSet);
	if (!s->read_copy (s, data, s->size)) {
		g_warning ("Strange: failed read");
		g_free (data);		
		return NULL;
	}

	if (!(vba_data = find_compressed_vba (data, s->size))) {
		g_free (data);
		return NULL;
	}

	if (MS_OLE_GET_GUINT8 (vba_data) != 1)
		g_warning ("Digit 0x%x != 1...", MS_OLE_GET_GUINT8 (vba_data));

	vba      = g_new0 (MsOleVba, 1);
	vba->s   = s;
	vba->pos = 0;

	decompress_vba (vba, vba_data, data + s->size);
	g_free (data);
	
	return vba;
}

/**
 * me_ols_vba_close:
 * @vba: 
 * 
 *   Free the resources associated with this vba
 * stream.
 **/
void
ms_ole_vba_close (MsOleVba *vba)
{
	if (vba) {
		g_array_free (vba->text, TRUE);
		vba->text = NULL;

		g_free (vba);
	}
}


--- Added File ms-ole-vba.h in package CMF ---
/**
 * ms-ole-vba.h: MS Office VBA support
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 *
 * Copyright 2000 Helix Code, Inc.
 **/

#ifndef MS_OLE_VBA_H
#define MS_OLE_VBA_H

#include <ms-ole.h>

typedef struct _MsOleVba MsOleVba;

MsOleVba *ms_ole_vba_open  (MsOleStream *s);
void      ms_ole_vba_close (MsOleVba    *vba);

char      ms_ole_vba_getc  (MsOleVba    *vba);
char      ms_ole_vba_peek  (MsOleVba    *vba);
gboolean  ms_ole_vba_eof   (MsOleVba    *vba);

#endif

--- Added File ms-ole.c in package CMF ---
/**
 * ms-ole.c: MS Office OLE support for Gnumeric
 *
 * Authors:
 *    Michael Meeks (michael@imaginator.com)
 *    Arturo Tena   (arturo@directmail.org)
 **/

#include <stdio.h>

/* BSDs require unistd.h before including stat.h */
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif

#include <sys/types.h>
#include <sys/stat.h>	/* for struct stat */
#include <fcntl.h>

#include <assert.h>
#include <ctype.h>
#include <glib.h>
#include <string.h>

#include <ms-ole.h>
#include "config.h"

#ifdef HAVE_MMAP
#include <sys/mman.h>
#endif

#ifdef HAVE_UNISTD_H
#	include <unistd.h>
#else
#	include <io.h>
#	include <sys/stat.h>
#	include <sys/types.h>
/*
#	define S_IRUSR 0000400
#	define S_IWUSR 0000200
#	define S_IRGRP 0000040
#	define S_IWGRP 0000020
#	define _S_ISREG(m) (((m)&0170000) == 0100000)
#	define S_ISREG(m) _S_ISREG(m)
#	define O_NONBLOCK 0x4000
*/
#endif

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

/* Implementational detail - not for global header */
#define OLE_DEBUG 0

/* 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 */

/* FIXME tenix laola reads this from the header */
#define BB_BLOCK_SIZE     512
#define SB_BLOCK_SIZE      64

/* 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
 */

MsOle *jp_msOle;

/**
 * Structure describing an OLE file
 **/
struct _MsOle
{
	int               ref_count;
	gboolean          ole_mmap;
	guint8           *mem;
	guint32           length;
	MsOleSysWrappers *syswrap;
	
	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 */
/* if memory mapped */
	GPtrArray        *bbattr;  /* Pointers to block structures */
/* end if memory mapped */

	unsigned char *buf;
	size_t len;
	size_t idx;
};



/**
 * Default system calls wrappers
 **/

static int
open2_wrap (const char *pathname, int flags)
{
	return open (pathname, flags);
}

static int
open3_wrap (const char *pathname, int flags, mode_t mode)
{
	return open (pathname, flags, mode);
}

static ssize_t
read_wrap (int fd, void *buf, size_t count)
{
	// printf(":read_wrap:\n");
	return read (fd, buf, count);
}

static int
close_wrap (int fd)
{
	return close (fd);
}

static ssize_t
write_wrap (int fd, const void *buf, size_t count)
{
	return write (fd, buf, count);
}

static off_t
lseek_wrap (int fd, off_t offset, int whence)
{
	return lseek (fd, offset, whence);
}

static int
isregfile_wrap (int fd)
{
	struct stat st;

	if (fstat (fd, &st))
		return 0;

	return S_ISREG(st.st_mode);
}

static int
getfilesize_wrap (int fd, guint32 *size)
{
	struct stat st;

	if (fstat (fd, &st))
		return -1;

	*size = st.st_size;
	return 0;
}
static MsOleSysWrappers default_wrappers = {
	open2_wrap,
	open3_wrap,
	read_wrap,
	close_wrap,
	write_wrap,
	lseek_wrap,
	isregfile_wrap,	
	getfilesize_wrap
};

//----------------------------------------------------------------------------//

static int open2_wrap_body (const char *pathname, int flags)
{
	int fd;
	fd = 3;
	return fd;
}

//----------------------------------------------------------------------------//

static int open3_wrap_body (const char *pathname, int flags, mode_t mode)
{
	int fd;
	fd = 3;
	return fd;
}

//----------------------------------------------------------------------------//

static ssize_t read_wrap_body (int fd, void *buf, size_t count)
{
	ssize_t temp;
	// printf(":read_wrap_body:\n");
	temp = count;
	memcpy(buf, &(jp_msOle->buf[jp_msOle->idx]), count);
	jp_msOle->idx = jp_msOle->idx +count;
	return temp;
}

//----------------------------------------------------------------------------//

static int close_wrap_body (int fd)
{
	int result;
	result = 0;
	return result;
}

//----------------------------------------------------------------------------//

static ssize_t write_wrap_body (int fd, const void *buf, size_t count)
{
	ssize_t temp;
	temp = write(fd, buf, count);
	return temp;
}

//----------------------------------------------------------------------------//

static off_t lseek_wrap_body (int fd, off_t offset, int whence)
{
	off_t temp;
	temp = offset;	
	jp_msOle->idx = offset;
	return temp;
}

//----------------------------------------------------------------------------//

static int isregfile_wrap_body (int fd)
{
	int result;
	struct stat st;
	result = 1;
	return result;
}

//----------------------------------------------------------------------------//

static int getfilesize_wrap_body (int fd, guint32 *size)
{
	int result;
	struct stat st;
	result = jp_msOle->len;
	*size = result;
	return 0;
}

//----------------------------------------------------------------------------//

static MsOleSysWrappers default_wrappers_body = 
{
	open2_wrap_body,
	open3_wrap_body,
	read_wrap_body,
	close_wrap_body,
	write_wrap_body,
	lseek_wrap_body,
	isregfile_wrap_body,	
	getfilesize_wrap_body
};

//----------------------------------------------------------------------------//

static void
take_wrapper_functions (MsOle *f, MsOleSysWrappers *wrappers) {
	if (wrappers == NULL)
		f->syswrap = &default_wrappers;
	else
		f->syswrap = wrappers;
}


/*
 * 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;

typedef guint32 PPS_IDX ;

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

static guint32
my_array_hack (GArray *a, guint s, guint32 idx)
{
	g_assert (a != NULL);
	g_assert (idx >= 0);
	g_assert (idx < a->len);
	g_assert (s == 4);
	return ((guint32 *)a->data)[idx];
}
#else
/* Far far faster... */
#       define ms_array_index(a,b,c) g_array_index (a, b, c)
#endif


typedef guint32 BLP;	/* Block pointer */


#define BB_THRESHOLD   0x1000

#define PPS_ROOT_INDEX    0
#define PPS_BLOCK_SIZE 0x80
#define PPS_END_OF_CHAIN 0xffffffff

typedef struct _PPS PPS;

#define PPS_SIG 0x13579753
#define IS_PPS(p) (((PPS *)(p))->sig == PPS_SIG)

struct _PPS {
	int      sig;
	char    *name;
	GList   *children;
	PPS     *parent;
	guint32  size;
	BLP      start;
	MsOleType type;
	PPS_IDX  idx; /* Only used on write */
};

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

#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))

#define MAX_CACHED_BLOCKS  32

typedef struct {
	guint32  blk;
	gboolean dirty;
	int      usage;
	guint8   *data;
} BBBlkAttr;

static BBBlkAttr *
bb_blk_attr_new (guint32 blk)
{
	BBBlkAttr *attr = g_new (BBBlkAttr, 1);
	attr->blk   = blk;
	attr->dirty = FALSE;
	attr->usage = 0;
	attr->data  = 0;
	return attr;
}

static void
write_cache_block (MsOle *f, BBBlkAttr *attr)
{
	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 (f->syswrap->lseek (f->file_des, offset, SEEK_SET)==(off_t)-1 ||
	    f->syswrap->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;
}

static guint8 *
get_block_ptr (MsOle *f, BLP b, gboolean forwrite)
{
	BBBlkAttr *attr, *tmp, *min;
	size_t offset;
	guint32 i, blks;

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

	/* Have we cached it ? */
	attr = g_ptr_array_index (f->bbattr, b);
	g_assert (attr);
	g_assert (attr->blk == b);

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

	/* 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 OLE_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);
	
	offset = (b+1)*BB_BLOCK_SIZE;
	f->syswrap->lseek (f->file_des, offset, SEEK_SET);
	f->syswrap->read (f->file_des, attr->data, BB_BLOCK_SIZE);
	attr->usage = 1;
	attr->dirty = forwrite;

	return attr->data;
}


/* 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.
   Each additional list can address 128*512/4*512 blocks ~= 8MBytes */
/* The number of Big Block Descriptor (fat) Blocks */
#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)))
/* The block locations of the Big Block Descriptor Blocks */
#define MAX_SIZE_BBD_LIST           109
/* FIXME tenix next is broken with big files */
#define GET_BBD_LIST(f,i)           (MS_OLE_GET_GUINT32((f)->mem + 0x4c + (i)*4))
/* FIXME tenix next is broken with big files */
#define SET_BBD_LIST(f,i,n)         (MS_OLE_SET_GUINT32((f)->mem + 0x4c + (i)*4, (n)))
#define NEXT_BB(f,n)                (g_array_index ((f)->bb, BLP, n))
#define NEXT_SB(f,n)                (g_array_index ((f)->sb, BLP, n))
/* 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))

/* Get the start block of the root directory ( PPS ) chain */
#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))
/* Get the start block of the SBD chain */
#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))


/* 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. */
#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))
/* These get other interesting stuff from the PPS record */
#define PPS_GET_STARTBLOCK(p)      ( MS_OLE_GET_GUINT32(p + 0x74))
#define PPS_GET_SIZE(p)            ( MS_OLE_GET_GUINT32(p + 0x78))
#define PPS_GET_TYPE(p) ((MsOleType)( MS_OLE_GET_GUINT8(p + 0x42)))
#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))

/* 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)) {                  \
        g_warning ("Tried to mark as unused the block %d which has %d\n",  \
                   (block), g_array_index ((f), BLP, (block)));             \
        } else { g_array_index ((f), BLP, (block)) = UNUSED_BLOCK; } }

/* FIXME: This needs proper unicode support ! current support is a guess */
/* Length is in bytes == 1/2 the final text length */
/* NB. Different from biff_get_text, looks like a bug ! */
static char *
pps_get_text (guint8 *ptr, int length)
{
	int lp;
	char *ans;
	guint16 c;
	guint8 *inb;
	
	length = (length+1)/2;

	if (length <= 0 ||
	    length > (PPS_BLOCK_SIZE/4)) {
#if OLE_DEBUG > 0
		printf ("Nulled name of length %d\n", length);
#endif
		return 0;
	}
	
	ans = (char *) g_malloc (sizeof (char) * length + 1);
	
	inb = ptr;
	for (lp = 0; lp < length; lp++) {
		c = MS_OLE_GET_GUINT16 (inb);
		ans [lp] = (char) c;
		inb += 2;
	}
	ans [lp] = 0;

	return ans;
}

static void
dump_header (MsOle *f)
{
	printf ("--------------------------MsOle HEADER-------------------------\n");
	printf ("Num BBD Blocks : %d Root %%d, SB blocks %d\n",
		f->bb?f->bb->len:-1,
/*		f->pps?f->pps->len:-1, */
/* FIXME tenix, here is not f->num_pps? */
		f->sb?f->sb->len:-1);
	printf ("-------------------------------------------------------------\n");
}

static void
characterise_block (MsOle *f, BLP blk, char **ans)
{
	int nblk;

	nblk = g_array_index (f->bb, BLP, blk);
	if (nblk == UNUSED_BLOCK) {
		*ans = "unused";
		return;
	} else if (nblk == SPECIAL_BLOCK) {
		*ans = "special";
		return;
	} else if (nblk == ADD_BBD_LIST_BLOCK) {
		*ans = "additional special";
		return;
	} else if (nblk == END_OF_CHAIN) {
		*ans = "end of chain";
		return;
	}

	*ans = "unknown";
	g_return_if_fail (f);
	g_return_if_fail (f->bb);
	g_return_if_fail (f->pps);

/*	for (lp=0;lp<f->pps->len;lp++) {
		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;
			}
			cur = NEXT_BB (f, cur);
		}
		}*/
}

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' (size: %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);
	}
}

static void
dump_allocation (MsOle *f)
{
	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);
	}
	
	if (f->pps) {
		printf ("Root blocks : %d\n", f->num_pps); 
		dump_tree (f->pps, 0);
	} else
		printf ("No root yet\n");
/*	
	printf ("sbd blocks : %d\n", h->sbd_list->len);
	for (lp=0;lp<h->sbd_list->len;lp++)
	printf ("sbd_list[%d] = %d\n", lp, (int)ms_array_index (h->sbd_list, SBPtr, lp));*/
	printf ("-------------------------------------------------------------\n");
}

/*
 * Dump some useful facts.
 * magic: 2       : dump tree
 *        default : dump header and allocation
 */
void
ms_ole_debug (MsOle *fs, int magic)
{
	switch (magic) {
	case 2:
		if (fs->pps)
			dump_tree (fs->pps, 0);
		else
			printf ("There are no tree (no pps)\n");
		break;
	default:
		dump_header (fs);
		dump_allocation (fs);
		break;
	}
}

/*
 * get_next_block:
 * @f:   the file handle
 * @blk: an index into the big block fat
 * 
 * Return value: the block index of the BBD block.
 */
static BLP
get_next_block (MsOle *f, BLP blk, gboolean *err)
{
	BLP bbd = GET_BBD_LIST (f, blk / (BB_BLOCK_SIZE / 4));

	if (bbd > (f->length / BB_BLOCK_SIZE)) {
		*err = TRUE;
		return 0;
	} else
		*err = FALSE;
	
	return MS_OLE_GET_GUINT32 (BB_R_PTR (f, bbd) +
				   4 * (blk % (BB_BLOCK_SIZE / 4)));
}

/* Builds the FAT */
static int
read_bb (MsOle *f)
{
	/* FIXME tenix may be later we wish to split this function */
	guint32  numbbd;
	BLP      lp;
	guint32  num_add_bbd_lists;
	BLP      missing_lps;
	BLP      missing_bbds;
	guint32  visited_add_bbd_list;
	BLP tmp;
	BLP bbd;

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

	f->bb   = g_array_new (FALSE, FALSE, sizeof(BLP));
	numbbd  = GET_NUM_BBD_BLOCKS  (f);

        /* Sanity checks */
/* FIXME tenix reading big files
	if (numbbd < ((f->length - BB_BLOCK_SIZE
		      + ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4) - 1)
			 / ((BB_BLOCK_SIZE*BB_BLOCK_SIZE)/4))) {
		printf ("Duff block descriptors\n");
		return 0;
	}
 */
	/* FIXME tenix check if size is small, there's no add bbd lists */
	
	/* Add BBD's that live in the BBD list */
	for (lp = 0; (lp < (f->length / BB_BLOCK_SIZE) - 1) &&
		     (lp < MAX_SIZE_BBD_LIST * BB_BLOCK_SIZE / 4); lp++) {
		gboolean err;

		tmp = get_next_block (f, lp, &err);
		if (err)
			return 0;

		g_array_append_val (f->bb, tmp);
	}

	/* 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) {
		if (lp != MAX_SIZE_BBD_LIST * BB_BLOCK_SIZE / 4)
			return 0;

		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
		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)))) {
				/* 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)
						+4*MAX_SIZE_ADD_BBD_LIST);
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
			}

			/* 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);
		}
		/* FIXME tenix do we check if we have visited all lp's but
		   there are more additional lists? */
	}

	/* 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);
	}
	if (num_add_bbd_lists > 0) {
		visited_add_bbd_list = GET_FIRST_ADD_BBD_LIST (f);
		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))) {
				/* 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)
						+ 4*MAX_SIZE_ADD_BBD_LIST);
				if (visited_add_bbd_list == END_OF_CHAIN) {
					if (lp + 1 != missing_lps) {
						/* FIXME tenix error */
					}
				}
				TRY_MARK_UNUSED_BLOCK (f->bb,
						       visited_add_bbd_list,
						       ADD_BBD_LIST_BLOCK);
			}

			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);
		}
	}

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

	/* More sanity checks */
/* FIXME
	for (lp=0; lp<numbbd; lp++) {
		BLP bbdblk = GET_BBD_LIST(f, lp);
		if (g_array_index(f->bb, BLP, bbdblk) != SPECIAL_BLOCK) {
			printf ("Error - BBD blocks not marked correctly\n");
			g_array_free (f->bb, TRUE);
			return 0;
		}
		}
*/

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

static void
extend_file (MsOle *f, guint blocks)
{
#ifdef HAVE_MMAP
	if (f->ole_mmap) {
		int file;
		guint8 *newptr, zero = 0;
		guint32 filesize;
		guint32 oldlen;
		guint32 icount;
		gchar zeroblock [BB_BLOCK_SIZE];

		memset (zeroblock, zero, BB_BLOCK_SIZE);
		g_assert (f);
		file = f->file_des;
		
		g_assert (munmap(f->mem, f->length) != -1);

		/* Extend that file by blocks */

		if (f->syswrap->getfilesize (file, &filesize)) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}
		if (f->syswrap->lseek (file, 0, SEEK_END) == (off_t)-1) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}
		for (icount = 0; icount < blocks; icount++) {
			if (f->syswrap->write (file, zeroblock, BB_BLOCK_SIZE -
					       ((icount == blocks - 1) ? 1 : 0))
			    == -1) {
				printf ("Serious error extending file\n");
				f->mem = 0;
				return;
			}
		}
		if (f->syswrap->write (file, &zero, 1) == -1) {
			printf ("Serious error extending file\n");
			f->mem = 0;
			return;
		}

		oldlen = filesize;

		if (f->syswrap->getfilesize (file, &(f->length))) {
			printf ("Warning couldn't get the size of the file\n");
		}
		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);
		/* NOTE tenix here we don't check if try_mmap is true, because
		   if it reach here it means f->ole_mmap is true and try_mmap is
		   true too. This is related with system wrappers. */
		newptr = mmap (f->mem, f->length, PROT_READ|PROT_WRITE, MAP_SHARED, file, 0);
#if OLE_DEBUG > 0
		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!");
		}
#endif /* OLE_DEBUG */
		f->mem = newptr;
	} else /* !f->ole_mmap */ {
#endif /* HAVE_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;
		}
#ifdef HAVE_MMAP
	}
#endif /* HAVE_MMAP */
}

static BLP
next_free_bb (MsOle *f)
{
	BLP blk, tblk;
  
	g_assert (f);

	blk = 0;
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
	while (blk < f->bb->len)
		if (g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK)
			return blk;
	        else 
			blk++;

	extend_file (f, 1);
	tblk = UNUSED_BLOCK;
	g_array_append_val (f->bb, tblk);
	g_assert ((g_array_index (f->bb, BLP, blk) == UNUSED_BLOCK));
	g_assert (f->bb->len < f->length/BB_BLOCK_SIZE);
	return blk;
}

static int
write_bb (MsOle *f)
{
	guint32 numbbd;
	BLP     lp, lpblk;
	int a = BB_BLOCK_SIZE / 4;

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

	numbbd = (f->bb->len + a - 2) / (a - 1); /* Think really hard! */
	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 */
		guint8 *mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
			     g_array_index (f->bb, BLP, lpblk));
		lpblk++;
	}
	while (lpblk % (BB_BLOCK_SIZE/4) != 0) { /* Undescribed blocks */
		guint8 *mem;
		g_assert (lpblk/(BB_BLOCK_SIZE/4) < numbbd);
		mem = BB_W_PTR(f, GET_BBD_LIST(f, lpblk/(BB_BLOCK_SIZE/4)));
		MS_OLE_SET_GUINT32 (mem + (lpblk%(BB_BLOCK_SIZE/4))*4,
			     UNUSED_BLOCK);
		lpblk++;
	}
	g_array_free (f->bb, TRUE);
	f->bb = 0;
	return 1;
}

static BLP
next_free_sb (MsOle *f)
{
	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);
		if (f->sbf->len > 0)
			g_array_index (f->bb, BLP, 
				       g_array_index (f->sbf, BLP, f->sbf->len-1)) = new_sbf;
		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;
}

static guint8 *
get_pps_ptr (MsOle *f, PPS_IDX i, gboolean forwrite)
{
	int lp;
	BLP blk = GET_ROOT_STARTBLOCK (f);

	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--;
		blk = NEXT_BB (f, blk);
	}
	if (blk == END_OF_CHAIN) {
		printf ("Serious error finding pps %d\n", i);
		return 0;
	}

#if OLE_DEBUG > 0
	printf ("get_pps_ptr: blk = %d\n", blk);
#endif
	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;
}

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);
	
	return g_strcasecmp (b->name, a->name);
}

static void
pps_decode_tree (MsOle *f, PPS_IDX p, PPS *parent)
{
	PPS    *pps;
	guint8 *mem;
       
	if (p == PPS_END_OF_CHAIN)
		return;

	pps           = g_new (PPS, 1);
	pps->sig      = PPS_SIG;
	mem           = get_pps_ptr (f, p, FALSE);
	if (!mem) {
		printf ("Serious directory error %d\n", p);
		f->pps = NULL;
		return;
	}
#if OLE_DEBUG > 0
	printf ("pps_decode_tree: mem (offset)= %#8.8x\n", mem - f->mem);
#endif
	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;
	pps->idx      = 0;
	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);
	}

	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);
	
#if OLE_DEBUG > 1
	printf ("PPS decode : '%s'\n", pps->name?pps->name:"Null");
	ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif
	return;
}

static int
read_pps (MsOle *f)
{
	PPS *pps;
	g_return_val_if_fail (f, 0);

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

	if (!f->pps || g_list_length (f->pps) < 1 ||
	    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;
	}

	/* 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");

	{ /* Free up the root chain */
		BLP blk, last;
		last = blk = GET_ROOT_STARTBLOCK (f);
		while (blk != END_OF_CHAIN) {
			last = blk;
			blk = NEXT_BB (f, blk);
			g_array_index (f->bb, BLP, last) = UNUSED_BLOCK;
		}
	}
	
	if (!f->pps) {
		printf ("Root directory too small\n");
		return 0;
	}
	return 1;
}

/**
 * Write the blocks main data recursively.
 **/
static void
pps_encode_tree_initial (MsOle *f, GList *list, PPS_IDX *p)
{
	int lp, max;
	guint8 *mem;
	PPS    *pps;

	g_return_if_fail (list);
	g_return_if_fail (list->data);
	
	pps = list->data;
	pps->idx = *p;
	(*p)++;

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

	mem = get_pps_ptr (f, pps->idx, TRUE);

	/* Blank stuff I don't understand */
	for (lp=0;lp<PPS_BLOCK_SIZE;lp++)
		MS_OLE_SET_GUINT8(mem+lp, 0);
	if (pps->name) {
		max = strlen (pps->name);
		if (max >= (PPS_BLOCK_SIZE/4))
			max = (PPS_BLOCK_SIZE/4);
		for (lp=0;lp<max;lp++)
			MS_OLE_SET_GUINT16(mem + lp*2, pps->name[lp]);
	} else {
		printf ("No name %d\n", *p);
		max = -1;
	}
	PPS_SET_NAME_LEN(mem, (max+1)*2);
	
	/* Magic numbers */
	if (pps->idx == PPS_ROOT_INDEX) { /* Only Root */
		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 ? */
	} else if (pps->size >= BB_THRESHOLD) {
		MS_OLE_SET_GUINT32  (mem + 0x50, 0x00020900);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x01);
	} else {
		MS_OLE_SET_GUINT32  (mem + 0x64, 0x09299c3c);
		MS_OLE_SET_GUINT32  (mem + 0x6c, 0x09299c3c);
		MS_OLE_SET_GUINT8   (mem + 0x43, 0x00);
	}

	PPS_SET_TYPE (mem, pps->type);
	PPS_SET_SIZE (mem, pps->size);
        PPS_SET_STARTBLOCK(mem, pps->start);
	PPS_SET_NEXT (mem, PPS_END_OF_CHAIN);
	PPS_SET_PREV (mem, PPS_END_OF_CHAIN);
	PPS_SET_DIR  (mem, PPS_END_OF_CHAIN);

#if OLE_DEBUG > 1
	printf ("Encode '%s' as \n", pps->name);
	ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif

	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
 */
static void
pps_encode_tree_chain (MsOle *f, GList *list)
{
	PPS	*parent;		/* parent's PPS */
	int	len;			/* how many childrens are there */
	GList	*lchildren;		/* visited children */
        PPS	*children;		/* visited children's PPS */
	PPS     *next;			/* next children's PPS */
	PPS	*prev;			/* previous children's PPS */
	guint8	*mem;			/* a PPS in memory */
	guint8	*mem_parent;		/* a PPS in memory */
	gint	i;
	int	half_way;

	g_return_if_fail (list);
	g_return_if_fail (list->data);

	parent = list->data;
	len = g_list_length (parent->children);
	half_way = len / 2;
	lchildren = parent->children;

	/* The base node of the directory */

	/* Choose the first child */
	mem_parent = get_pps_ptr (f, parent->idx, TRUE);

	if (len == 1) {
		PPS_SET_DIR (mem_parent, ((PPS *)(lchildren->data))->idx);

#if OLE_DEBUG > 1
		printf ("tenix3 Final encode '%s' as \n",
			((PPS *)(parent))->name);
		ms_ole_dump (mem_parent, PPS_BLOCK_SIZE);
		printf ("tenix3 Final encode '%s' as \n",
			((PPS *)(lchildren->data))->name);
		ms_ole_dump (get_pps_ptr (f, ((PPS *)(lchildren->data))->idx, FALSE),
		      PPS_BLOCK_SIZE);
#endif

		return;
	}

#if OLE_DEBUG > 1
	if (len == 0)
		printf ("Empty directory '%s'\n", ((PPS *)(children))->name);
#endif

	i = 0;
	for (; lchildren; lchildren = g_list_next (lchildren)) {
		children = lchildren->data;

		if (children->type == MsOleStorageT)
			pps_encode_tree_chain (f, lchildren);

		if (i == half_way)
			PPS_SET_DIR (mem_parent, ((PPS *)(children))->idx);

		mem = get_pps_ptr (f, children->idx, TRUE);
		if (i == half_way) {
			if (g_list_previous (lchildren)) {
				prev = g_list_previous(lchildren)->data;
				PPS_SET_PREV (mem, prev->idx);
			}

			if (g_list_next (lchildren)) {
				next = g_list_next (lchildren)->data;
				PPS_SET_NEXT (mem, next->idx);
			}
		} else if (i < half_way) {
			if (g_list_previous(lchildren)) {
				prev = g_list_previous (lchildren)->data;
				PPS_SET_PREV (mem, prev->idx);
			}
		} else /* i > half_way */ {
			if (g_list_next(lchildren)) {
				next = g_list_next (lchildren)->data;
				PPS_SET_NEXT (mem, next->idx);
			}
		}

#if OLE_DEBUG > 1
			printf ("tenix1 Final encode '%s' as \n",
				((PPS *)(children))->name);
			ms_ole_dump (mem, PPS_BLOCK_SIZE);
#endif

		i++;
	}

#if OLE_DEBUG > 1
	printf ("tenix2 Final encode '%s' as \n", ((PPS *)(parent))->name);
	ms_ole_dump (mem_parent, PPS_BLOCK_SIZE);
#endif
}


static int
write_pps (MsOle *f)
{
	int lp;
	PPS_IDX idx;
	BLP blk  = END_OF_CHAIN;
	BLP last = END_OF_CHAIN;

	/* 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 {
#if OLE_DEBUG > 0
			printf ("Set root block to %d\n", blk);
#endif
			SET_ROOT_STARTBLOCK (f, blk);
		}
		g_array_index (f->bb, BLP, blk) = END_OF_CHAIN;
	}

	g_assert (GET_ROOT_STARTBLOCK(f) != END_OF_CHAIN);

	idx    = PPS_ROOT_INDEX;
	pps_encode_tree_initial (f, f->pps, &idx);
	pps_encode_tree_chain   (f, f->pps);

	f->pps = 0;
	f->num_pps = 0;
	return 1;
}

static int
read_sb (MsOle *f)
{
	BLP ptr;
	int lastidx, idx;
	PPS *root;

	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->pps, 0);

	root = f->pps->data;
	g_return_val_if_fail (root, 0);

	f->sbf = g_array_new (FALSE, FALSE, sizeof(BLP));
	f->sb  = g_array_new (FALSE, FALSE, sizeof(BLP));
	
	/* List of big blocks in SB file */
	ptr = root->start;
#if OLE_DEBUG > 0
	printf ("Starting Small block file at %d\n", root->start);
#endif
	while (ptr != END_OF_CHAIN) {
		if (ptr == UNUSED_BLOCK ||
		    ptr == SPECIAL_BLOCK) {
			printf ("Corrupt small block file: serious error, "
				"invalid block in chain\n");
			g_array_free (f->sbf, TRUE);
			f->sbf = 0;
			return 0;
		}
		g_array_append_val (f->sbf, ptr);
		ptr = NEXT_BB (f, ptr);
	}

	/* Description of small blocks */
	lastidx = -1;
	idx     = 0;
	ptr = GET_SBD_STARTBLOCK (f);

	if (f->sbf->len == 0 && ptr != END_OF_CHAIN) {
		printf ("No small block file, but small block depot start block exists!: "
			"ignore depot, since there's no small block files after all.\n");
		ptr = END_OF_CHAIN;
	}

	while (ptr != END_OF_CHAIN) {
		guint32 lp;
		if (ptr == UNUSED_BLOCK ||
		    ptr == SPECIAL_BLOCK) {
			printf ("Corrupt file descriptor: serious error, "
				"invalid block in chain\n");
			g_array_free (f->sb, TRUE);
			f->sb = 0;
			return 0;
		}
		for (lp=0;lp<BB_BLOCK_SIZE/4;lp++) {
			BLP p = MS_OLE_GET_GUINT32 (BB_R_PTR(f, ptr) + lp*4);
			g_array_append_val (f->sb, p);
			
			if (p != UNUSED_BLOCK)
				lastidx = idx;
			idx++;
		}
		ptr = NEXT_BB (f, ptr);
	}
	if (lastidx>0)
		g_array_set_size (f->sb, lastidx+1);
	
	if (f->sbf->len * BB_BLOCK_SIZE < f->sb->len*SB_BLOCK_SIZE) {
		printf ("Not enough small block file for descriptors\n"
			"sbf->len == %d, sb->len == %d\n", f->sbf->len,
			f->sb->len);
		return 0;
	}

	return 1;
}

static int
write_sb (MsOle *f)
{
	guint32 lp, lastused;
	PPS *root;
	BLP sbd_start  = END_OF_CHAIN;
	BLP sbf_start  = END_OF_CHAIN;

	g_return_val_if_fail (f, 0);
	g_return_val_if_fail (f->pps, 0);

	root = f->pps->data;

	if (f->sbf->len * BB_BLOCK_SIZE < f->sb->len*SB_BLOCK_SIZE) {
		printf ("Not enough descriptor / blocks being written %d %d\n",
			f->sbf->len, f->sb->len);
	}
	if (f->sbf->len>0)
		sbf_start = g_array_index (f->sbf, BLP, 0);

	lastused = END_OF_CHAIN;
	for (lp=0;lp<f->sb->len;lp++) {
		if (g_array_index (f->sb, BLP, lp) != UNUSED_BLOCK)
			lastused = lp;
	}

	if (lastused != END_OF_CHAIN) { /* Bother writing stuff */
		guint8 *mem = 0;
		guint32 num_sbdf = (lastused + (BB_BLOCK_SIZE/4)-1) /
			(BB_BLOCK_SIZE/4);
		BLP blk = END_OF_CHAIN, last;

#if OLE_DEBUG > 0
		printf ("Num SB descriptor blocks : %d\n", num_sbdf);
#endif

		for (lp=0;lp<num_sbdf*(BB_BLOCK_SIZE/4);lp++) {
			BLP set;
			if (lp%(BB_BLOCK_SIZE/4) == 0) {
				last = blk;
				blk = next_free_bb(f);
				if (!lp)
					sbd_start = blk;
				if (last != END_OF_CHAIN)
					g_array_index (f->bb, BLP, last) = blk;
				g_array_index (f->bb, BLP, blk) = END_OF_CHAIN;
				mem = BB_W_PTR (f, blk);
			}
			if (lp<f->sb->len)
				set = g_array_index (f->sb, BLP, lp);
			else
				set = UNUSED_BLOCK;
			MS_OLE_SET_GUINT32 (mem + (lp%(BB_BLOCK_SIZE/4))*4, set);
		}
	} else {
#if OLE_DEBUG > 0
		printf ("Blank SB allocation\n");
#endif
		sbf_start = END_OF_CHAIN;
	}

	root->start = sbf_start;
	SET_SBD_STARTBLOCK (f, sbd_start);
	g_array_free (f->sb,  TRUE);
	g_array_free (f->sbf, TRUE);
	f->sb       = 0;
	f->sbf      = 0;
	return 1;
}

static int
ms_ole_setup (MsOle *f)
{
	if (!f->ole_mmap) {
		guint32 i;
		f->bbattr = g_ptr_array_new ();
		for (i = 0; i <(f->length / BB_BLOCK_SIZE) + 1; i++)
			g_ptr_array_add (f->bbattr, bb_blk_attr_new(i));
	}
	
	if (read_bb  (f) &&
	    read_pps (f) &&
	    read_sb  (f)) {
#if OLE_DEBUG > 1
		printf ("Just read header of\n");
		dump_header (f);
#endif		
		return 1;
	}
	return 0;
}

static int
ms_ole_cleanup (MsOle *f)
{
	if (f->mode != 'w') /* Nothing to write */
		return 1;
#if OLE_DEBUG > 1
	printf ("About to write header of: \n");
	dump_header (f);
#endif
	if (write_sb  (f) &&
	    write_pps (f) &&
	    write_bb  (f))
		return 1;
	return 0;
}

static MsOle *
new_null_msole ()
{
	MsOle *f = g_new0 (MsOle, 1);

	f->mem    = (guint8 *)0xdeadbeef;
	f->length = 0;
	f->mode   = 'r';
	f->bb     = 0;
	f->bbattr = 0;
	f->sb     = 0;
	f->sbf    = 0;
	f->pps    = 0;
	f->dirty  = 0;

	return f;
}


/**
 * ms_ole_ref:
 * @fs: filesystem object.
 * 
 * Increment by one the count of references to the filesystem.
 **/
void
ms_ole_ref (MsOle *fs)
{
	g_return_if_fail (fs != NULL);
	fs->ref_count++;
}


/**
 * ms_ole_unref:
 * @fs: filesystem object.
 * 
 * Decrement by one the count of references to the filesystem.
 **/
void
ms_ole_unref (MsOle *fs)
{
	g_return_if_fail (fs != NULL);
	fs->ref_count--;
}


/**
 * ms_ole_open_vfs:
 * @fs: filesystem object.
 * @path: path to the filesystem-in-the file on the actual filesystem.
 * @try_mmap: TRUE if try to mmap(2) the filesystem-in-a-file,
 *            instead of opening.
 * @wrappers: system functions wrappers, %NULL if standard functions are used.
 * 
 * Opens the filesystem-in-the-file @path and creates the filesystem object @fs.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_open_vfs (MsOle **f, const char *name, gboolean try_mmap,
		 MsOleSysWrappers *wrappers)
{
#ifdef HAVE_MMAP
	int prot = PROT_READ | PROT_WRITE;
#endif
	int file;

	if (!f)
		return MS_OLE_ERR_BADARG;

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif

	*f = new_null_msole();
	take_wrapper_functions (*f, wrappers);
	(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDWR);
	(*f)->ref_count = 0;
	(*f)->mode = 'w';
	if (file == -1) {
		(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDONLY);
		(*f)->mode = 'r';
		prot &= ~PROT_WRITE;
	}
	if ((file == -1) || !((*f)->syswrap->isregfile (file))) {
		/* FIXME tenix is not a memory leak not to close file? */
		printf ("No such file '%s'\n", name);
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->syswrap->getfilesize( file, &((*f)->length) )) {
		printf ("Couldn't get the size of file '%s'\n", name);
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->length <= 0x4c) { /* Bad show */
#if OLE_DEBUG > 0
		printf ("File '%s' too short\n", name);
#endif
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_FORMAT;
	}

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, prot, MAP_SHARED, file, 0);

		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

			if (!(*f)->mem
			    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
				== -1)) {
				printf ("Error reading header\n");
				g_free (*f);
				return MS_OLE_ERR_EXIST;
			}
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

		if (!(*f)->mem
		    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
			== -1)) {
			printf ("Error reading header\n");
			g_free (*f);
			return MS_OLE_ERR_EXIST;
		}
	} /* try_mmap */
	
	if (MS_OLE_GET_GUINT32((*f)->mem    ) != 0xe011cfd0 ||
	    MS_OLE_GET_GUINT32((*f)->mem + 4) != 0xe11ab1a1) {
#if OLE_DEBUG > 0
		printf ("Failed OLE2 magic number %x %x\n",
			MS_OLE_GET_GUINT32((*f)->mem), MS_OLE_GET_GUINT32((*f)->mem+4));
#endif
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file '%s':%d non-integer number of blocks\n",
			name, (*f)->length);

	if (!ms_ole_setup (*f)) {
		printf ("'%s' : duff file !\n", name);
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}

	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif
	/* If writing then when destroy commit it */
	return MS_OLE_ERR_OK;
}


MsOleErr ms_ole_open_vfs_body (MsOle **f, const char *name, gboolean try_mmap, MsOleSysWrappers *wrappers, char *jp_buf_ptr, size_t jp_buf_len)
{
#ifdef HAVE_MMAP
	int prot = PROT_READ | PROT_WRITE;
#endif
	int file;

	if (!f)
		return MS_OLE_ERR_BADARG;

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif

	*f = new_null_msole();

	jp_msOle = *f;

	(*f)->buf = jp_buf_ptr;
	(*f)->len = jp_buf_len;
	(*f)->idx = 0;
		
	// take_wrapper_functions (*f, wrappers);
	take_wrapper_functions (*f, &default_wrappers_body);
		
	(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDWR);
	(*f)->ref_count = 0;
	(*f)->mode = 'w';
	
	if (file == -1) {
		(*f)->file_des = file = (*f)->syswrap->open2 (name, O_RDONLY);
		(*f)->mode = 'r';
		prot &= ~PROT_WRITE;
	}
	if ((file == -1) || !((*f)->syswrap->isregfile (file))) {
		/* FIXME tenix is not a memory leak not to close file? */
		printf ("No such file '%s'\n", name);
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->syswrap->getfilesize( file, &((*f)->length) )) {
		printf ("Couldn't get the size of file '%s'\n", name);
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_EXIST;
	}
	if ((*f)->length <= 0x4c) { /* Bad show */
#if OLE_DEBUG > 0
		printf ("File '%s' too short\n", name);
#endif
		(*f)->syswrap->close (file) ;
		g_free (*f) ;
		return MS_OLE_ERR_FORMAT;
	}

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, prot, MAP_SHARED, file, 0);

		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

			if (!(*f)->mem
			    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
				== -1)) {
				printf ("Error reading header\n");
				g_free (*f);
				return MS_OLE_ERR_EXIST;
			}
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem = g_new (guint8, BB_BLOCK_SIZE);

		if (!(*f)->mem
		    || ((*f)->syswrap->read (file, (*f)->mem, BB_BLOCK_SIZE)
			== -1)) {
			printf ("Error reading header\n");
			g_free (*f);
			return MS_OLE_ERR_EXIST;
		}
	} /* try_mmap */
	
	if (MS_OLE_GET_GUINT32((*f)->mem    ) != 0xe011cfd0 ||
	    MS_OLE_GET_GUINT32((*f)->mem + 4) != 0xe11ab1a1) {
#if OLE_DEBUG > 0
		printf ("Failed OLE2 magic number %x %x\n",
			MS_OLE_GET_GUINT32((*f)->mem), MS_OLE_GET_GUINT32((*f)->mem+4));
#endif
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}
	
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file '%s':%d non-integer number of blocks\n", name, (*f)->length);

	if (!ms_ole_setup (*f)) {
		printf ("'%s' : duff file !\n", name);
		ms_ole_destroy (f);
		return MS_OLE_ERR_FORMAT;
	}

	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

#if OLE_DEBUG > 0
	printf ("New OLE file '%s'\n", name);
#endif
	/* If writing then when destroy commit it */
	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_create_vfs:
 * @fs: filesystem object.
 * @path: path to the filesystem-in-the file on the actual filesystem.
 * @try_mmap: TRUE if try to mmap(2) the filesystem-in-a-file,
 *            instead of opening.
 * @wrappers: system functions wrappers, %NULL if standard functions are used.
 * 
 * Creates the filesystem-in-the-file @path and creates the filesystem @fs.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_create_vfs (MsOle **f, const char *name, gboolean try_mmap,
		   MsOleSysWrappers *wrappers)
{
	int file, zero=0;
	int init_blocks = 1, lp;

	if (!f)
		return MS_OLE_ERR_BADARG;

	*f = new_null_msole ();
	take_wrapper_functions (*f, wrappers);
	if ((file = (*f)->syswrap->open3 (name,
					  O_RDWR|O_CREAT|O_TRUNC|O_NONBLOCK,
					  S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP))
	    == -1) {
		printf ("Can't create file '%s'\n", name);
		g_free (*f);
		*f = NULL;
		return MS_OLE_ERR_PERM;
	}

	if (((*f)->syswrap->lseek (file, BB_BLOCK_SIZE * init_blocks - 1,
		    SEEK_SET) == (off_t)-1) ||
	    ((*f)->syswrap->write (file, &zero, 1) == -1)) {
		printf ("Serious error extending file to %d bytes\n",
			BB_BLOCK_SIZE*init_blocks);
		g_free (*f);
		*f = NULL;
		return MS_OLE_ERR_SPACE;
	}

	(*f)->ref_count = 0;
	(*f)->file_des  = file;
	(*f)->mode      = 'w';
	if ((*f)->syswrap->getfilesize (file, &((*f)->length))) {
		printf ("Warning couldn't get the size of the file '%s'\n",
			name);
	}
	if ((*f)->length % BB_BLOCK_SIZE)
		printf ("Warning file %d non-integer number of blocks\n",
			(*f)->length);

	if (try_mmap) {
#ifdef HAVE_MMAP
		(*f)->ole_mmap = TRUE;
		(*f)->mem = mmap (0, (*f)->length, PROT_READ|PROT_WRITE,
				  MAP_SHARED, file, 0);
		if (!(*f)->mem || (caddr_t)(*f)->mem == (caddr_t)MAP_FAILED) {
#endif
			// g_warning ("I can't mmap that file, falling back to slower method");
			(*f)->ole_mmap = FALSE;
			(*f)->mem  = g_new (guint8, BB_BLOCK_SIZE);
#ifdef HAVE_MMAP
		}
#endif
	} else /* !try_mmap */ {
		// g_warning ("I won't mmap that file, using a slower method");
		(*f)->ole_mmap = FALSE;
		(*f)->mem  = g_new (guint8, BB_BLOCK_SIZE);
	} /* try_mmap */

	/* The header block */
	for (lp = 0; lp < BB_BLOCK_SIZE / 4; lp++)
		MS_OLE_SET_GUINT32((*f)->mem + lp * 4,
				   (lp < (0x52 / 4)) ? 0: UNUSED_BLOCK);

	MS_OLE_SET_GUINT32((*f)->mem,     0xe011cfd0); /* Magic number */
	MS_OLE_SET_GUINT32((*f)->mem + 4, 0xe11ab1a1);

	/* More magic numbers */
	MS_OLE_SET_GUINT32((*f)->mem + 0x18, 0x0003003e);
	MS_OLE_SET_GUINT32((*f)->mem + 0x1c, 0x0009fffe);
	MS_OLE_SET_GUINT32((*f)->mem + 0x20, 0x6); 
	MS_OLE_SET_GUINT32((*f)->mem + 0x38, 0x00001000); 
/*	MS_OLE_SET_GUINT32((*f)->mem + 0x40, 0x1);  */
	MS_OLE_SET_GUINT32((*f)->mem + 0x44, 0xfffffffe); 

	SET_NUM_BBD_BLOCKS  (*f, 0);
	SET_ROOT_STARTBLOCK (*f, END_OF_CHAIN);
	SET_SBD_STARTBLOCK  (*f, END_OF_CHAIN);

	{
		PPS *p;

		(*f)->bb  = g_array_new (FALSE, FALSE, sizeof(BLP));
		(*f)->sb  = g_array_new (FALSE, FALSE, sizeof(BLP));
		(*f)->sbf = g_array_new (FALSE, FALSE, sizeof(BLP));
		p           = g_new(PPS, 1);
		p->sig      = PPS_SIG;
		p->name     = g_strdup ("Root Entry");
		p->start    = END_OF_CHAIN;
		p->type     = MsOleRootT;
		p->size     = 0;
		p->children = NULL;
		p->parent   = NULL;
		(*f)->pps = g_list_append (0, p);
		(*f)->num_pps = 1;

		if ((*f)->ole_mmap)
			(*f)->bbattr = NULL;
		else
			(*f)->bbattr   = g_ptr_array_new ();
	}
	g_assert ((*f)->bb->len < (*f)->length/BB_BLOCK_SIZE);

	return MS_OLE_ERR_OK;
}


static void
destroy_pps (GList *l)
{
	GList *tmp;

	for (tmp = l; tmp; tmp = g_list_next (tmp)) {
		PPS *pps = tmp->data;
		if (pps->name)
			g_free (pps->name);
		destroy_pps (pps->children);
		g_free (pps);
	}
	g_list_free (l);
}


/**
 * ms_ole_destroy:
 * @fs: filesystem object.
 * 
 * Closes the filesystem @fs and truncates any free blocks.
 **/
void
ms_ole_destroy (MsOle **ptr)
{
	MsOle *f = *ptr;

#if OLE_DEBUG > 0
	printf ("FIXME: should truncate to remove unused blocks\n");
#endif
	if (f) {
		if (f->ref_count != 0)
			g_warning ("Unclosed files exist on this OLE stream");

		if (f->dirty)
			ms_ole_cleanup (f);

		if (f->mem == (void *)0xdeadbeef)
		    f->mem = NULL;
		else if (f->ole_mmap) {
#ifdef HAVE_MMAP
			munmap (f->mem, f->length);
#else
			g_warning ("Unmapping while we dont have mmap call");
#endif
		} else {
			guint32 i;
			for (i = 0; (f->bbattr) && (i < f->bbattr->len); i++) {
				BBBlkAttr *attr = g_ptr_array_index (f->bbattr, i);
				if (f->dirty && attr->dirty)
					write_cache_block (f, attr);
				g_free (attr->data);
				attr->data = 0;
			}
			if (f->dirty) {
				f->syswrap->lseek (f->file_des, 0, SEEK_SET);
				f->syswrap->write (f->file_des, f->mem,
						   BB_BLOCK_SIZE);
			}
			g_free (f->mem);
			f->mem = 0;
		}

		destroy_pps (f->pps);

		f->syswrap->close (f->file_des);
		g_free (f);

#if OLE_DEBUG > 0
		printf ("Closing OLE file\n");
#endif
	}
	*ptr = NULL;
}


/**
 * ms_ole_dump:
 * @ptr: memory area to be dumped.
 * @len: how many bytes will be dumped.
 * 
 * Dump @len bytes from the memory location given by @ptr.
 **/
void
ms_ole_dump (guint8 const *ptr, guint32 len)
{
	guint32 lp,lp2;
	guint32 off;

	for (lp = 0;lp<(len+15)/16;lp++)
	{
		printf ("%8x | ", lp*16);
		for (lp2=0;lp2<16;lp2++) {
			off = lp2 + (lp<<4);
			off<len?printf("%2x ", ptr[off]):printf("XX ");
		}
		printf ("| ");
		for (lp2=0;lp2<16;lp2++) {
			off = lp2 + (lp<<4);
			printf ("%c", off<len?(ptr[off]>'!'&&ptr[off]<127?ptr[off]:'.'):'*');
		}
		printf ("\n");
	}
}


/*
 * Redundant stream check function.
 */
static void
check_stream (MsOleStream *s)
{
	BLP blk;
	guint32 idx;
	PPS *p;
	MsOle *f;

	g_return_if_fail (s);
	g_return_if_fail (s->file);

	f = s->file;
	p = s->pps;

	g_return_if_fail (p);
	blk = p->start;
	idx = 0;
	if (s->type == MsOleSmallBlock) {
		while (blk != END_OF_CHAIN) {
			g_assert (g_array_index (s->blocks, BLP, idx) ==
				  blk);
#if OLE_DEBUG > 2
			ms_ole_dump (GET_SB_R_PTR(f, blk), SB_BLOCK_SIZE);
#endif
			blk = NEXT_SB (f, blk);
			idx++;
		}
	} else {
		while (blk != END_OF_CHAIN) {
			g_assert (g_array_index (s->blocks, BLP, idx) ==
				  blk);
#if OLE_DEBUG > 2
			ms_ole_dump (BB_R_PTR(f, blk), BB_BLOCK_SIZE);
#endif
			blk = NEXT_BB (f, blk);
			idx++;
		}
	}
}


static MsOlePos
tell_pos (MsOleStream *s)
{
	return s->position;
}


/*
 * Free the allocation chains, and free up the blocks.
 * "It was for freedom that Christ has set us free."
 *   Galatians 5:11
 */
static void
free_allocation (MsOle *f, guint32 startblock, gboolean is_big_block_stream)
{
	g_return_if_fail (f);

#if OLE_DEBUG > 0
	printf ("Free allocation %d : (%d)\n", startblock,
		is_big_block_stream);
#endif
       
	if (is_big_block_stream)
	{
		BLP p = startblock;
		printf ("FIXME: this should also free up blocks\n");
		while (p != END_OF_CHAIN) {
			BLP next = NEXT_BB (f,p);
			if (next == p) {
				printf ("Serious bug: cyclic ring in BB allocation\n");
				return;
			} else if (p == SPECIAL_BLOCK ||
				   p == UNUSED_BLOCK) {
				printf ("Serious bug: Special / Unused block "
					"in BB allocation\n");
				return;
			}
			g_array_index (f->bb, BLP, p) = UNUSED_BLOCK;
			p = next;
		}
	}
	else
	{
		BLP p = startblock;
		while (p != END_OF_CHAIN) {
			BLP next = NEXT_SB (f,p);
			if (next == p) {
				printf ("Serious bug: cyclic ring in SB allocation\n");
				return;
			} else if (p == SPECIAL_BLOCK ||
				   p == UNUSED_BLOCK) {
				printf ("Serious bug: Special / Unused block "
					"in SB allocation\n");
				return;
			}
			g_array_index (f->sb, BLP, p) = UNUSED_BLOCK;
			p = next;
		}
		/* Seek forwards to find blank sbf blocks */
		{
			guint32 lp;
			BLP     lastused = END_OF_CHAIN;
			for (lp=0;lp<f->sb->len;lp++) {
				if (g_array_index (f->sb, BLP, lp) != UNUSED_BLOCK)
					lastused = lp;
			}
			if (lastused == END_OF_CHAIN) {
				for (lp=0;lp<f->sbf->len;lp++) {
					BLP sbfd = g_array_index (f->sbf, BLP, lp);
					g_array_index (f->bb, BLP, sbfd) = UNUSED_BLOCK;
				}
				g_array_set_size (f->sbf, 0);
				g_array_set_size (f->sb, 0);
			} else {
				guint32 sbf_needed = (lastused+(BB_BLOCK_SIZE/SB_BLOCK_SIZE)-1) /
					             (BB_BLOCK_SIZE/SB_BLOCK_SIZE);

				if (sbf_needed == f->sbf->len)
					return;
				
				for (lp=sbf_needed;lp<f->sbf->len;lp++) {
					BLP sbfd = g_array_index (f->sbf, BLP, lp);
					g_array_index (f->bb, BLP, sbfd) = UNUSED_BLOCK;
				}
				g_array_set_size (f->sbf, sbf_needed);
				g_array_set_size (f->sb, lastused+1);
			}
		}
	}
}


/**
 * ms_ole_lseek:
 * @s: stream object.
 * @bytes: number of bytes to set the stream pointer.
 * @type: relative from where the stream pointer will be set.
 * 
 * Set the stream pointer for @s as many as @bytes bytes according to @type.
 * 
 * Return value: the new position of the stream pointer.
 **/
static MsOleSPos
ms_ole_lseek (MsOleStream *s, MsOleSPos bytes, MsOleSeek type)
{
	/* FIXME tenix improve limits detection: avoid gint vs guint limits */
	MsOleSPos newpos;

	g_return_val_if_fail (s, -1);

	newpos = s->position;
	if (type == MsOleSeekSet)
		newpos  = bytes;
	else if (type == MsOleSeekCur)
		newpos += bytes;
	else
		newpos = s->size - bytes;

	if (newpos > s->size || newpos < 0) {
		g_warning ("Invalid seek");
		return -1;
	}
	s->position = newpos;
	return newpos;
}


/*
 *  Returns:
 *  NULL    - on error
 */
static guint8*
ms_ole_read_ptr_bb (MsOleStream *s, MsOlePos length)
{
	int blklen;
	guint8 *ans;
	guint32 len=length;
	int blockidx = s->position/BB_BLOCK_SIZE;

	g_return_val_if_fail (s, NULL);

	if (!s->blocks || blockidx >= s->blocks->len) {
		printf ("Reading from NULL file\n");
		return NULL;
	}

	blklen = BB_BLOCK_SIZE - s->position%BB_BLOCK_SIZE;

	if (len > blklen && !s->file->ole_mmap)
		return NULL;

	while (len > blklen) {
		len -= blklen;
		blklen = BB_BLOCK_SIZE;
		if (blockidx >= (s->blocks->len - 1)
		    || (ms_array_index (s->blocks, BLP, blockidx)
			!= blockidx + 1))
			return NULL;
		blockidx++;
	}
	/* Straight map, simply return a pointer */
	ans = BB_R_PTR (s->file, ms_array_index (s->blocks, BLP,
						 s->position/BB_BLOCK_SIZE))
		+ s->position%BB_BLOCK_SIZE;
	ms_ole_lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return ans;
}


/*
 *  Returns:
 *  NULL    - on error
 */
static guint8*
ms_ole_read_ptr_sb (MsOleStream *s, MsOlePos length)
{
	int blklen;
	guint8 *ans;
	guint32 len=length;
	int blockidx = s->position/SB_BLOCK_SIZE;

	g_return_val_if_fail (s, NULL);

	if (!s->blocks || blockidx >= s->blocks->len) {
		printf ("Reading from NULL file\n");
		return NULL;
	}

	blklen = SB_BLOCK_SIZE - s->position%SB_BLOCK_SIZE;

	if (len > blklen && !s->file->ole_mmap)
		return NULL;

	while (len > blklen) {
		len -= blklen;
		blklen = SB_BLOCK_SIZE;
		if (blockidx >= (s->blocks->len - 1)
		    || (ms_array_index (s->blocks, BLP, blockidx)
			!= blockidx + 1))
			return NULL;
		blockidx++;
	}
	/* Straight map, simply return a pointer */
	ans = GET_SB_R_PTR (s->file, ms_array_index (s->blocks, BLP,
						     s->position/SB_BLOCK_SIZE))
		+ s->position%SB_BLOCK_SIZE;
	ms_ole_lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return ans;
}


/*
 *  Returns:
 *  zero    - on error
 *  no zero - on success
 */
static gint
ms_ole_read_copy_bb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8 *src;
	int offset = s->position % BB_BLOCK_SIZE;
	int blkidx = s->position / BB_BLOCK_SIZE;

	g_return_val_if_fail (s, 0);
	g_return_val_if_fail (ptr, 0);

	
	if (!s->blocks) {
		printf ("Reading from NULL file\n");
		return 0;
	}

	while (length > 0)
	{
		BLP block;
		int cpylen = BB_BLOCK_SIZE - offset;
		if (cpylen > length)
			cpylen = length;

		if (s->position + cpylen > s->size
		    || blkidx == s->blocks->len) {
#if OLE_DEBUG > 0
			printf ("Trying 2 to read beyond end of stream %d+%d %d\n",
				s->position, cpylen, s->size);
#endif
			return 0;
		}
		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		src = BB_R_PTR (s->file, block) + offset;
		
		memcpy (ptr, src, cpylen);
		ptr    += cpylen;
		length -= cpylen;
		
		offset = 0;
		
		blkidx++;
		s->position+=cpylen;
	}

	if (libole2_debug)
		check_stream (s);

	return 1;
}


/*
 *  Returns:
 *  zero    - on error
 *  no zero - on success
 */
static gint
ms_ole_read_copy_sb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	int offset = s->position%SB_BLOCK_SIZE;
	int blkidx = s->position/SB_BLOCK_SIZE;
	guint8 *src;

	g_return_val_if_fail (s, 0);
	g_return_val_if_fail (ptr, 0);

	
	if (!s->blocks) {
		printf ("Reading from NULL file\n");
		return 0;
	}

	while (length > 0)
	{
		int cpylen = SB_BLOCK_SIZE - offset;
		BLP block;
		if (cpylen>length)
			cpylen = length;
		if (s->position + cpylen > s->size
		    || blkidx == s->blocks->len) {
#if OLE_DEBUG > 0
			printf ("Trying 3 to read beyond end of stream %d+%d %d\n",
				s->position, cpylen, s->size);
#endif
			return 0;
		}
		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		src = GET_SB_R_PTR(s->file, block) + offset;
		
		memcpy (ptr, src, cpylen);
		ptr += cpylen;
		length -= cpylen;
		
		offset = 0;

		blkidx++;
		s->position+=cpylen;
	}

	if (libole2_debug)
		check_stream (s);

	return 1;
}


static void
ms_ole_append_block (MsOleStream *s)
{
	BLP block;
	BLP lastblk = END_OF_CHAIN;
	BLP eoc     = END_OF_CHAIN;

	if (s->type==MsOleSmallBlock) {
		if (!s->blocks)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));

		else if (s->blocks->len>0)
			lastblk = ms_array_index (s->blocks, BLP, s->blocks->len-1);

		block = next_free_sb (s->file);
		g_array_append_val (s->blocks, block);

		if (lastblk != END_OF_CHAIN) { /* Link onwards */
			g_array_index (s->file->sb, BLP, lastblk) = block;
#if OLE_DEBUG > 1
			printf ("Chained Small block %d to previous block %d\n", block, lastblk);
#endif
		} else { /* First block in a file */
			PPS *p = s->pps;
#if OLE_DEBUG > 0
			printf ("Set first Small block to %d\n", block);
#endif
			p->start = block;
		}

		g_array_index (s->file->sb, BLP, block) = eoc;
	} else {
		if (!s->blocks)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));
		else if (s->blocks->len>0)
			lastblk = ms_array_index (s->blocks, BLP, s->blocks->len-1);

		block = next_free_bb (s->file);
#if OLE_DEBUG > 0
		{
			int lp;
			g_assert (g_array_index (s->file->bb, BLP, block) == UNUSED_BLOCK);
			for (lp=0;lp<s->blocks->len;lp++)
				g_assert (g_array_index (s->blocks, BLP, lp) != block);
		}
#endif
		g_array_append_val (s->blocks, block);

		if (lastblk != END_OF_CHAIN) { /* Link onwards */
			g_array_index (s->file->bb, BLP, lastblk) = block;
#if OLE_DEBUG > 1
			printf ("Chained Big block %d to block %d\n", block, lastblk);
#endif
		} else { /* First block in a file */
			PPS *p = s->pps;
#if OLE_DEBUG > 0
			printf ("Set first Big block to %d\n", block);
#endif
			p->start = block;
		}

		g_array_index (s->file->bb, BLP, block) = eoc;
	}
}


/* FIXME: I'm sure these functions should fail gracefully somehow :-) */
static MsOlePos
ms_ole_write_bb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8  *dest;
	gint32   lengthen;
	guint32  bytes   = length;
	int      offset  = s->position%BB_BLOCK_SIZE;
	guint32  blkidx  = s->position/BB_BLOCK_SIZE;
	
	s->file->dirty = 1;
	while (bytes > 0) {
		BLP block;
		int cpylen = BB_BLOCK_SIZE - offset;
		if (cpylen > bytes)
			cpylen = bytes;
		
		if (!s->blocks || blkidx >= s->blocks->len)
			ms_ole_append_block (s);

		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		
		dest = BB_W_PTR(s->file, block) + offset;

#if OLE_DEBUG > 1
		printf ("Copy %d bytes to block %d\n", cpylen, block);
#endif
		memcpy (dest, ptr, cpylen);
		ptr   += cpylen;
		bytes -= cpylen;
		
		offset = 0;
		blkidx++;
	}

	lengthen = s->position - s->size + length;
	if (lengthen > 0)
		s->size += lengthen;

	s->lseek (s, length, MsOleSeekCur);

	if (libole2_debug)
		check_stream (s);

	return length;
}


/* FIXME: I'm sure these functions should fail gracefully somehow :-) */
static MsOlePos
ms_ole_write_sb (MsOleStream *s, guint8 *ptr, MsOlePos length)
{
	guint8 *dest;
	int     offset  = s->position%SB_BLOCK_SIZE;
	guint32 blkidx  = s->position/SB_BLOCK_SIZE;
	guint32 bytes   = length;
	gint32  lengthen;
	
	s->file->dirty = 1;
	while (bytes > 0) {
		BLP block;
		int cpylen = SB_BLOCK_SIZE - offset;

		if (cpylen > bytes)
			cpylen = bytes;
		
		if (!s->blocks || blkidx >= s->blocks->len)
			ms_ole_append_block (s);
		g_assert (s->blocks);

		g_assert (blkidx < s->blocks->len);
		block = ms_array_index (s->blocks, BLP, blkidx);
		
		dest = GET_SB_W_PTR(s->file, block) + offset;
		
		g_assert (cpylen >= 0);

		memcpy (dest, ptr, cpylen);
		ptr   += cpylen;
		bytes -= cpylen;

		lengthen = s->position + length - bytes - s->size;
		if (lengthen > 0)
			s->size += lengthen;
		
		/* Must be exactly filling the block */
		if (s->size >= BB_THRESHOLD)
		{
			PPS         *p = s->pps;
			MsOlePos oldlen;
			guint8      *buffer;

			buffer       = g_new (guint8, s->size);
			s->lseek     (s, 0, MsOleSeekSet);
			oldlen       = s->size;
			s->read_copy (s, buffer, oldlen);

			free_allocation (s->file, p->start, 0);
			p->start    = END_OF_CHAIN;
#if OLE_DEBUG > 1
			printf ("\n\n--- Converting ---\n\n\n");
#endif
			s->read_copy = ms_ole_read_copy_bb;
			s->read_ptr  = ms_ole_read_ptr_bb;
			s->lseek     = ms_ole_lseek;
			s->tell      = tell_pos;
			s->write     = ms_ole_write_bb;

			g_assert (s->size % SB_BLOCK_SIZE == 0);

			/* Convert the file to BBlocks */
			s->size     = 0;
			s->position = 0;
			s->type  = MsOleLargeBlock;
			g_array_free (s->blocks, TRUE);
			s->blocks   = 0;

			s->write (s, buffer, oldlen);

			/* Continue the interrupted write */
			ms_ole_write_bb (s, ptr, bytes);
			bytes = 0;
#if OLE_DEBUG > 1
			printf ("\n\n--- Done ---\n\n\n");
#endif
			g_free (buffer);
			return length;
		}
		
		offset = 0;
		blkidx++;

		if (libole2_debug)
			check_stream (s);
	}
	s->lseek (s, length, MsOleSeekCur);

	return length;
}


/**
 * pps_create:
 * @f: ole file handle.
 * @p: returned pps.
 * @parent: parent pps.
 * @name: its name.
 * @type: the type.
 * 
 * Creates a storage or stream.
 * 
 * Return value: error status.
 **/
static MsOleErr
pps_create (MsOle *f, GList **p, GList *parent, const char *name,
	    MsOleType type)
{
	PPS *pps, *par;

	if (!p || !parent || !parent->data || !name) {
		g_warning ("duff arguments to pps_create");
		return MS_OLE_ERR_BADARG;
	}

	pps  = g_new (PPS, 1);
	if (!pps)
		return MS_OLE_ERR_MEM;
	
	pps->sig      = PPS_SIG;
	pps->name     = g_strdup (name);
	pps->type     = type;
	pps->size     = 0;
	pps->start    = END_OF_CHAIN;
	pps->children = NULL;
	pps->parent   = parent->data;
	
	par = (PPS *)parent->data;
	par->children = g_list_insert_sorted (par->children, pps,
					      (GCompareFunc)pps_compare_func);
	*p = g_list_find (par->children, pps);
	f->num_pps++;

	return MS_OLE_ERR_OK;
}


/**
 * find_in_pps:
 * @l: the parent storage chain element.
 * 
 * Find the right Stream ... John 4:13-14 ...
 * in a storage
 * 
 * Return value: %NULL if not found or pointer to the child list
 **/
static GList *
find_in_pps (GList *l, const char *name)
{
	PPS   *pps;
	GList *cur;

	g_return_val_if_fail (l != NULL, NULL);
	g_return_val_if_fail (l->data != NULL, NULL);
	pps = l->data;
	g_return_val_if_fail (IS_PPS (pps), NULL);
	
	if (pps->type == MsOleStorageT ||
	    pps->type == MsOleRootT)
		cur = pps->children;
	else {
		g_warning ("trying to enter a stream '%s'",
			   pps->name?pps->name:"no name");
		return NULL;
	}
	
	for ( ;cur ; cur = g_list_next (cur)) {
		PPS *pps = cur->data;
		g_return_val_if_fail (IS_PPS (pps), NULL);
		
		if (!pps->name)
			continue;
		
		if (!g_strcasecmp (pps->name, name))
			return cur;
	}
	return NULL;
}


/**
 * path_to_pps:
 * @pps:  pointer to pps to return value in.
 * @f:    ole file hande.
 * @path: path to find.
 * @file: file to find in path.
 * @create_if_not_found: create the pps with the given path if not found.
 * 
 * Locates a stream or storage with the given path.
 * 
 * Return value: a #MsOleErr code.
 **/
static MsOleErr
path_to_pps (PPS **pps, MsOle *f, const char *path,
	     const char *file,
	     gboolean create_if_not_found)
{
	guint     lp;
	gchar   **dirs;
	GList    *cur, *parent;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);
	
	dirs = g_strsplit (path, "/", -1);
	g_return_val_if_fail (dirs != NULL, MS_OLE_ERR_BADARG);

	parent = cur = f->pps;

	for (lp = 0; dirs[lp]; lp++) {
		if (dirs[lp][0] == '\0' || !cur) {
			g_free (dirs[lp]);
			continue;
		}

		parent = cur;

		cur = find_in_pps (parent, dirs[lp]);
		
		if (!cur && create_if_not_found &&
		    pps_create (f, &cur, parent, dirs[lp], MsOleStorageT) !=
		    MS_OLE_ERR_OK)
			cur = NULL;
		/* else carry on not finding them before dropping out */

		g_free (dirs[lp]);
	}
	g_free (dirs);

	if (!cur || !cur->data)
		return MS_OLE_ERR_EXIST;

	if (file[0] == '\0') { /* We just want a directory */
		*pps = cur->data;
		g_return_val_if_fail (IS_PPS (cur->data), MS_OLE_ERR_INVALID);
		return MS_OLE_ERR_OK;
	}

	parent = cur;
	cur = find_in_pps (parent, file);

	/* now the file */
	if (!cur) {
		if (create_if_not_found) {
			MsOleErr result;
			result = pps_create (f, &cur, parent, file,
					     MsOleStreamT);
			if (result == MS_OLE_ERR_OK) {
				*pps = cur->data;
				g_return_val_if_fail (IS_PPS (cur->data),
						      MS_OLE_ERR_INVALID);
				return MS_OLE_ERR_OK;
			} else
				return result;
		}
		return MS_OLE_ERR_EXIST;
	}

	if (cur && cur->data) {
		*pps = cur->data;
		g_return_val_if_fail (IS_PPS (cur->data), MS_OLE_ERR_INVALID);
		return MS_OLE_ERR_OK;
	}

	return MS_OLE_ERR_EXIST;
}


/**
 * ms_ole_unlink:
 * @fs: filesystem object.
 * @path: path of the stream or directory to delete.
 *
 * Delete the stream or directory @path on the filesystem @fs.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_unlink (MsOle *f, const char *path)
{
	g_warning ("Unimplemented");

	/* FIXME missing implementation, or at least a better error code =-) */
	return MS_OLE_ERR_NOTEMPTY;
}


/**
 * ms_ole_directory:
 * @names: array where the names are storesd, it's %NULL ended.
 * @fs: filesystem object.
 * @dirpath: directory path.
 *
 * Gets the names of the streams and directories in the directory @dirpath.
 *
 * Returns: a #MsOleErr code.
 **/
MsOleErr
ms_ole_directory (char ***names, MsOle *f, const char *path)
{
	char    **ans;
	PPS      *pps;
	MsOleErr  result;
	GList    *l;
	int       lp;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);

	if ((result = path_to_pps (&pps, f, path, "", FALSE)) !=
	    MS_OLE_ERR_OK)
		return result;

	if (!pps)
		return MS_OLE_ERR_INVALID;

	l   = pps->children;
	ans = g_new (char *, g_list_length (l) + 1);
	
	lp = 0;
	for (; l; l = g_list_next (l)) {
		pps = (PPS *)l->data;

		if (!pps->name)
			continue;

		ans[lp] = g_strdup (pps->name);
		lp++;
	}
	ans[lp] = NULL;

	*names = ans;
	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stat:
 * @stat: stat information.
 * @fs: filesystem object.
 * @dirpath: directory path.
 * @name: stream or directory name.
 *
 * Gets information about the stream or the directory which is in the directory
 * @dirpath and its name is @file.
 *
 * Returns: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stat (MsOleStat *stat, MsOle *f, const char *path,
	     const char *file)
{
	PPS      *pps;
	MsOleErr  result;

	g_return_val_if_fail (f != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (file != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (path != NULL, MS_OLE_ERR_BADARG);
	g_return_val_if_fail (stat != NULL, MS_OLE_ERR_BADARG);

	if ((result = path_to_pps (&pps, f, path, file, FALSE)) !=
	    MS_OLE_ERR_OK)
		return result;

	if (!pps)
		return MS_OLE_ERR_INVALID;

	stat->type = pps->type;
	stat->size = pps->size;

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_open:
 * @stream: stream object.
 * @fs: filesystem object.
 * @dirpath: directory of the stream.
 * @name: stream name.
 * @mode: mode of opening stream.
 * 
 * Opens the stream in @dirpath with the name @name and creates the stream
 * object @stream. If @mode is '%r' it opens read only, and if it is '%w'
 * it opens for write only.
 *
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_open (MsOleStream ** const stream, MsOle *f,
		    const char *path, const char *fname, char mode)
{
	PPS         *p;
	MsOleStream *s;
	int lp, panic=0;
	MsOleErr     result;

	if (!stream)
		return MS_OLE_ERR_BADARG;
	*stream = NULL;

	if (!path || !f)
		return MS_OLE_ERR_BADARG;

	if (mode == 'w' && f->mode != 'w') {
		printf ("Opening stream '%c' when file is '%c' only\n",
			mode, f->mode);
		return MS_OLE_ERR_PERM;
	}

	if ((result = path_to_pps (&p, f, path, fname, (mode == 'w'))) !=
	    MS_OLE_ERR_OK)
		return result;

	s           = g_new0 (MsOleStream, 1);
	s->file     = f;
	s->pps      = p;
	s->position = 0;
	s->size     = p->size;
	s->blocks   = NULL;

#if OLE_DEBUG > 0
	printf ("Parsing blocks\n");
#endif
	if (s->size >= BB_THRESHOLD) {
		BLP b = p->start;

		s->read_copy = ms_ole_read_copy_bb;
		s->read_ptr  = ms_ole_read_ptr_bb;
		s->lseek     = ms_ole_lseek;
		s->tell      = tell_pos;
		s->write     = ms_ole_write_bb;

		s->blocks    = g_array_new (FALSE, FALSE, sizeof(BLP));
		s->type   = MsOleLargeBlock;
		for (lp = 0; !panic & (lp < (s->size + BB_BLOCK_SIZE - 1) / BB_BLOCK_SIZE); lp++) {
			g_array_append_val (s->blocks, b);
#if OLE_DEBUG > 1
			printf ("Block %d\n", b);
#endif
			if (b == END_OF_CHAIN ||
			    b == SPECIAL_BLOCK ||
			    b == UNUSED_BLOCK) {

				printf ("Panic: broken stream, truncating to block %d\n", lp);
				s->size = (lp-1)*BB_BLOCK_SIZE;
				panic   = 1;

#if OLE_DEBUG > 0
				if (b == END_OF_CHAIN)
					printf ("Warning: bad file length in '%s'\n", p->name);
				else if (b == SPECIAL_BLOCK)
					printf ("Warning: special block in '%s'\n", p->name);
				else if (b == UNUSED_BLOCK)
					printf ("Warning: unused block in '%s'\n", p->name);
#endif
			} else
				b = NEXT_BB (f, b);
		}
		if (b != END_OF_CHAIN) {
			BLP next;
			printf ("Panic: extra unused blocks on end of '%s', wiping it\n",
				p->name);
			while (b != END_OF_CHAIN &&
			       b != UNUSED_BLOCK &&
			       b != SPECIAL_BLOCK) {
				next = NEXT_BB (f, b);
				g_array_index (f->bb, BLP, b) = END_OF_CHAIN;
				b = next;
			}
		}
	} else {
		BLP b = p->start;

		s->read_copy = ms_ole_read_copy_sb;
		s->read_ptr  = ms_ole_read_ptr_sb;
		s->lseek     = ms_ole_lseek;
		s->tell      = tell_pos;
		s->write     = ms_ole_write_sb;

		if (s->size>0)
			s->blocks = g_array_new (FALSE, FALSE, sizeof(BLP));
		else
			s->blocks = NULL;

		s->type   = MsOleSmallBlock;

		for (lp = 0; !panic & (lp < (s->size + SB_BLOCK_SIZE - 1) / SB_BLOCK_SIZE); lp++) {
			g_array_append_val (s->blocks, b);
#if OLE_DEBUG > 0
			printf ("Block %d\n", b);
#endif
			if (b == END_OF_CHAIN ||
			    b == SPECIAL_BLOCK ||
			    b == UNUSED_BLOCK) {

				printf ("Panic: broken stream, truncating to block %d\n", lp);
				s->size = (lp-1)*SB_BLOCK_SIZE;
				panic   = 1;
#if OLE_DEBUG > 0
				if (b == END_OF_CHAIN)
					printf ("Warning: bad file length in '%s'\n", p->name);
				else if (b == SPECIAL_BLOCK)
					printf ("Warning: special block in '%s'\n", p->name);
				else if (b == UNUSED_BLOCK)
					printf ("Warning: unused block in '%s'\n", p->name);
#endif
			} else
				b = NEXT_SB (f, b);
		}
		if (b != END_OF_CHAIN) {
			BLP next;
			printf ("Panic: extra unused blocks on end of '%s', wiping it\n",
				p->name);
			while (b != END_OF_CHAIN &&
			       b != UNUSED_BLOCK &&
			       b != SPECIAL_BLOCK) {
				next = NEXT_SB (f, b);
				g_array_index (f->sb, BLP, b) = END_OF_CHAIN;
				b = next;
			}
			if (b != END_OF_CHAIN)
				printf ("Panic: even more serious block error\n");
		}
	}
	*stream = s;
	ms_ole_ref (s->file);

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_duplicate:
 * @stream_copy: stream object copy.
 * @stream: stream object to be duplicated.
 * 
 * Duplicates the stream object @stream.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_duplicate (MsOleStream **s, const MsOleStream * const stream)
{
	if (!s || !stream)
		return MS_OLE_ERR_BADARG;

	/* Lets face it having two pointers to the same file is a nightmare anyway :-) */
	g_warning ("Do NOT use this function, it is unsafe with the blocks array");

	if (!(*s = g_new (MsOleStream, 1)))
		return MS_OLE_ERR_MEM;

	memcpy (*s, stream, sizeof (MsOleStream));
	ms_ole_ref (stream->file);

	return MS_OLE_ERR_OK;
}


/**
 * ms_ole_stream_close:
 * @stream: stream object to be closed.
 * 
 * Closes the @stream.
 * 
 * Return value: a #MsOleErr code.
 **/
MsOleErr
ms_ole_stream_close (MsOleStream **s)
{
	if (*s) {
		if ((*s)->file && (*s)->file->mode == 'w')
			((PPS *)(*s)->pps)->size = (*s)->size;

		if ((*s)->blocks)
			g_array_free ((*s)->blocks, TRUE);

		ms_ole_unref ((*s)->file);

		g_free (*s);
		*s = NULL;

		return MS_OLE_ERR_OK;
	}
	return MS_OLE_ERR_BADARG;
}

--- Added File ms-ole.h in package CMF ---
/**
 * ms-ole.h: MS Office OLE support for Gnumeric
 *
 * Authors:
 *    Michael Meeks (michael@imaginator.com)
 *    Arturo Tena (arturo@directmail.org)
 **/

#ifndef MS_OLE_H
#define MS_OLE_H

/* This should be done in glib */
#ifndef _WIN32
#	include <fcntl.h>	/* for mode_t */
#else
	typedef unsigned long mode_t;
	typedef size_t ssize_t;
	typedef /* signed */ long off_t;
#endif
#include <glib.h>

typedef enum {
	MS_OLE_ERR_OK,
	MS_OLE_ERR_EXIST,
	MS_OLE_ERR_INVALID,
	MS_OLE_ERR_FORMAT,
	MS_OLE_ERR_PERM,
	MS_OLE_ERR_MEM,
	MS_OLE_ERR_SPACE,
	MS_OLE_ERR_NOTEMPTY,
	MS_OLE_ERR_BADARG
} MsOleErr;

typedef enum {
	MsOleSeekSet,
	MsOleSeekCur,
	MsOleSeekEnd
} MsOleSeek;

typedef enum  {
	MsOleStorageT = 1,
	MsOleStreamT  = 2,
	MsOleRootT    = 5
} MsOleType;

typedef guint32 MsOlePos;
typedef gint32  MsOleSPos;
/*
#ifdef G_HAVE_GINT64
	typedef guint64 MsOlePos;
	typedef gint64  MsOleSPos;
#else
	typedef guint32 MsOlePos;
	typedef gint32  MsOleSPos;
#endif
*/


typedef struct _MsOle             MsOle;
typedef struct _MsOleStat         MsOleStat;
typedef struct _MsOleStream       MsOleStream;
typedef struct _MsOleSysWrappers  MsOleSysWrappers;

struct _MsOleSysWrappers {
	int     (*open2)	(const char *pathname, int flags);
	int     (*open3)	(const char *pathname, int flags, mode_t mode);
	ssize_t (*read)		(int fd, void *buf, size_t count);
	int     (*close)	(int fd);
	ssize_t (*write)	(int fd, const void *buf, size_t count);
	off_t   (*lseek)	(int fd, off_t offset, int whence);
	int     (*isregfile)	(int fd);
	int     (*getfilesize)	(int fd, guint32 *size);
};

struct _MsOleStat {
	MsOleType type;
	MsOlePos  size;
};

#define                 ms_ole_open(fs,path)     ms_ole_open_vfs ((fs), (path), TRUE, NULL)
extern MsOleErr		ms_ole_open_vfs		(MsOle **fs,
						 const char *path,
						 gboolean try_mmap,
						 MsOleSysWrappers *wrappers);
#define                 ms_ole_create(fs,path)   ms_ole_create_vfs ((fs), (path), TRUE, NULL)
extern MsOleErr		ms_ole_create_vfs	(MsOle **fs,
						 const char *path,
						 int try_mmap,
						 MsOleSysWrappers *wrappers);
extern void		ms_ole_destroy		(MsOle **fs);
extern MsOleErr		ms_ole_unlink		(MsOle *fs,
						 const char *path);
extern MsOleErr		ms_ole_directory	(char ***names,
						 MsOle *fs,
						 const char *dirpath);
extern MsOleErr		ms_ole_stat		(MsOleStat *stat,
						 MsOle *fs,
						 const char *dirpath,
						 const char *name);

struct _MsOleStream {

	MsOlePos size;

	gint		(*read_copy)	(MsOleStream *stream,
					 guint8 *ptr,
					 MsOlePos length);

	guint8 *	(*read_ptr)	(MsOleStream *stream,
					 MsOlePos length);

	MsOleSPos	(*lseek)	(MsOleStream *stream,
					 MsOleSPos bytes,
					 MsOleSeek type);

	MsOlePos	(*tell)		(MsOleStream *stream);

	MsOlePos	(*write)	(MsOleStream *stream,
					 guint8 *ptr,
					 MsOlePos length);


	/**
	 * Private.
	 **/
	enum {
		MsOleSmallBlock,
		MsOleLargeBlock
	} type;
	MsOle		*file;
	void		*pps;		/* Straight PPS */
	GArray		*blocks;	/* A list of the blocks in the file
					   if NULL: no file */
	MsOlePos	 position;	/* Current offset into file.
					   Points to the next byte to read */
};

#define MS_OLE_GET_GUINT8(p)  (*((const guint8 *)(p) + 0))
#define MS_OLE_GET_GUINT16(p) (guint16)(*((const guint8 *)(p)+0) |        \
					(*((const guint8 *)(p)+1)<<8))
#define MS_OLE_GET_GUINT32(p) (guint32)(*((const guint8 *)(p)+0) |        \
					(*((const guint8 *)(p)+1)<<8) |   \
					(*((const guint8 *)(p)+2)<<16) |  \
					(*((const guint8 *)(p)+3)<<24))
#define MS_OLE_GET_GUINT64(p) (MS_OLE_GET_GUINT32(p) | \
			       (((guint32)MS_OLE_GET_GUINT32((const guint8 *)(p)+4))<<32))

#define MS_OLE_SET_GUINT8(p,n)  (*((guint8 *)(p) + 0) = n)
#define MS_OLE_SET_GUINT16(p,n) ((*((guint8 *)(p)+0)=((n)&0xff)),         \
				 (*((guint8 *)(p)+1)=((n)>>8)&0xff))
#define MS_OLE_SET_GUINT32(p,n) ((*((guint8 *)(p)+0)=((n))&0xff),         \
				 (*((guint8 *)(p)+1)=((n)>>8)&0xff),      \
				 (*((guint8 *)(p)+2)=((n)>>16)&0xff),     \
				 (*((guint8 *)(p)+3)=((n)>>24)&0xff))

extern MsOleErr		ms_ole_stream_open	(MsOleStream ** const stream,
						 MsOle *fs,
						 const char *dirpath,
						 const char *name,
						 char mode);
extern MsOleErr		ms_ole_stream_close	(MsOleStream ** const stream);
extern MsOleErr		ms_ole_stream_duplicate	(MsOleStream ** const stream_copy,
						 const MsOleStream *
						 const stream);

extern void		ms_ole_dump		(guint8 const *ptr,
						 guint32 len);

extern void		ms_ole_ref		(MsOle *fs);
extern void		ms_ole_unref		(MsOle *fs);
extern void		ms_ole_debug		(MsOle *fs,
						 int magic);



#endif	/* MS_OLE_H */

--- Added File ole.c in package CMF ---
//----------------------------------------------------------------------------//
// ole.c
// John Platten
// 01.12.01
//----------------------------------------------------------------------------//

#include <stdio.h>
#include "ms-ole.h"
#include "ms-ole-summary.h"

//----------------------------------------------------------------------------//
// GLOBALS
//----------------------------------------------------------------------------//

char str[512];

//----------------------------------------------------------------------------//
// PROTOTYPES
//----------------------------------------------------------------------------//

MsOle * _ms_ole_open (char *fileName);

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len);

void _ms_ole_destroy (MsOle *ole);

MsOleSummary * _ms_ole_summary_open (MsOle *ole);

void _ms_ole_summary_close (MsOleSummary *summary);

MsOleSummary * _ms_ole_docsummary_open (MsOle *f);

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId);

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId);

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId);

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId);

//----------------------------------------------------------------------------//

MsOle * _ms_ole_open (char *fileName)
{
	MsOle *ole = NULL;
	
	ms_ole_open_vfs(&ole, fileName, FALSE, NULL);
	
	// if (!ole) fprintf(stderr, ":ERROR:ms_ole_open_vfs:msOleErr:%x\n", ole);
		
	return ole;
}

//----------------------------------------------------------------------------//

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len)
{
	MsOle * msOle = NULL;
	MsOleErr msOleErr;
	char * fileName = "junk";
			
	msOleErr = ms_ole_open_vfs_body(&msOle, fileName, FALSE, NULL, jp_buf_ptr, jp_buf_len);
	
	// if (!msOle) fprintf(stderr, ":ERROR:ms_ole_open_vfs:msOleErr:%x\n", msOleErr);

	return msOle;
}

//----------------------------------------------------------------------------//

void _ms_ole_destroy (MsOle *ole)
{
    MsOle * myOle = ole;
    
    ms_ole_destroy(&myOle);
}

//----------------------------------------------------------------------------//

MsOleSummary * _ms_ole_summary_open (MsOle *ole)
{
	MsOleSummary *summary = NULL;
	
	summary = ms_ole_summary_open(ole);
	
	// if (!summary) fprintf(stderr, ":ERROR:_ms_ole_summary_open:summary:%x\n", summary);
		
	return summary;
}

//----------------------------------------------------------------------------//

void _ms_ole_summary_close (MsOleSummary *summary)
{
    ms_ole_summary_close(summary);
}

//----------------------------------------------------------------------------//

MsOleSummary * _ms_ole_docsummary_open (MsOle *f)
{
    return ms_ole_docsummary_open(f);
}

//----------------------------------------------------------------------------//

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId)
{
	char *str = NULL;
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_string(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId)
{
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_long(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId)
{
	gboolean ret = FALSE;
	
    return ms_ole_summary_get_short(summary, propertyId, &ret);
}

//----------------------------------------------------------------------------//

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId)
{
	GTimeVal timeval;
	gboolean ret = FALSE;
	
	timeval = ms_ole_summary_get_time(summary, propertyId, &ret);

	strcpy(str, ctime(&timeval.tv_sec));
	str[strlen(str)-1] = '\0';

	return str;
}

//----------------------------------------------------------------------------//

--- Added File ole.i in package CMF ---
%module ole
%{
#include "ms-ole.h"
#include "ms-ole-summary.h"
%}

MsOle * _ms_ole_open (char *fileName);

MsOle * _ms_ole_open_body (char * jp_buf_ptr, int jp_buf_len);

void _ms_ole_destroy (MsOle *ole);

MsOleSummary * _ms_ole_summary_open (MsOle *ole);

void _ms_ole_summary_close (MsOleSummary *summary);

MsOleSummary * _ms_ole_docsummary_open (MsOle *f);

char * _ms_ole_summary_get_string (MsOleSummary *summary, int propertyId);

int _ms_ole_summary_get_long (MsOleSummary *summary, int propertyId);

short int _ms_ole_summary_get_short (MsOleSummary *summary, int propertyId);

char * _ms_ole_summary_get_time (MsOleSummary *summary, int propertyId);

--- Added File ole_wrap.c in package CMF ---
/*
 * FILE : ole_wrap.c
 * 
 * This file was automatically generated by :
 * Simplified Wrapper and Interface Generator (SWIG)
 * Version 1.1 (Build 883)
 * 
 * Portions Copyright (c) 1995-1998
 * The University of Utah and The Regents of the University of California.
 * Permission is granted to distribute this file in any manner provided
 * this notice remains intact.
 * 
 * Do not make changes to this file--changes will be lost!
 *
 */


#define SWIGCODE
/* Implementation : PYTHON */

#define SWIGPYTHON
/***********************************************************************
 * $Header: /cvs-repository/Packages/Products/DCProject/CMF_MS_Files/src/libole/ole_wrap.c,v 1.1 2001/05/30 14:39:19 jack Exp $
 * swig_lib/python/python.cfg
 *
 * Contains variable linking and pointer type-checking code.
 ************************************************************************/

#include <string.h>
#include <stdlib.h>

#ifdef __cplusplus
extern "C" {
#endif
#include "Python.h"

/* Definitions for Windows/Unix exporting */
#if defined(_WIN32) || defined(__WIN32__)
#   if defined(_MSC_VER)
#	define SWIGEXPORT(a) __declspec(dllexport) a
#   else
#	if defined(__BORLANDC__)
#	    define SWIGEXPORT(a) a _export
#	else
#	    define SWIGEXPORT(a) a
#	endif
#   endif
#else
#   define SWIGEXPORT(a) a
#endif

#ifdef SWIG_GLOBAL
#define SWIGSTATICRUNTIME(a) SWIGEXPORT(a)
#else
#define SWIGSTATICRUNTIME(a) static a
#endif

typedef struct {
  char  *name;
  PyObject *(*get_attr)(void);
  int (*set_attr)(PyObject *);
} swig_globalvar;

typedef struct swig_varlinkobject {
  PyObject_HEAD
  swig_globalvar **vars;
  int      nvars;
  int      maxvars;
} swig_varlinkobject;

/* ----------------------------------------------------------------------
   swig_varlink_repr()

   Function for python repr method
   ---------------------------------------------------------------------- */

static PyObject *
swig_varlink_repr(swig_varlinkobject *v)
{
  v = v;
  return PyString_FromString("<Global variables>");
}

/* ---------------------------------------------------------------------
   swig_varlink_print()

   Print out all of the global variable names
   --------------------------------------------------------------------- */

static int
swig_varlink_print(swig_varlinkobject *v, FILE *fp, int flags)
{

  int i = 0;
  flags = flags;
  fprintf(fp,"Global variables { ");
  while (v->vars[i]) {
    fprintf(fp,"%s", v->vars[i]->name);
    i++;
    if (v->vars[i]) fprintf(fp,", ");
  }
  fprintf(fp," }\n");
  return 0;
}

/* --------------------------------------------------------------------
   swig_varlink_getattr
 
   This function gets the value of a variable and returns it as a
   PyObject.   In our case, we'll be looking at the datatype and
   converting into a number or string
   -------------------------------------------------------------------- */

static PyObject *
swig_varlink_getattr(swig_varlinkobject *v, char *n)
{
  int i = 0;
  char temp[128];

  while (v->vars[i]) {
    if (strcmp(v->vars[i]->name,n) == 0) {
      return (*v->vars[i]->get_attr)();
    }
    i++;
  }
  sprintf(temp,"C global variable %s not found.", n);
  PyErr_SetString(PyExc_NameError,temp);
  return NULL;
}

/* -------------------------------------------------------------------
   swig_varlink_setattr()

   This function sets the value of a variable.
   ------------------------------------------------------------------- */

static int
swig_varlink_setattr(swig_varlinkobject *v, char *n, PyObject *p)
{
  char temp[128];
  int i = 0;
  while (v->vars[i]) {
    if (strcmp(v->vars[i]->name,n) == 0) {
      return (*v->vars[i]->set_attr)(p);
    }
    i++;
  }
  sprintf(temp,"C global variable %s not found.", n);
  PyErr_SetString(PyExc_NameError,temp);
  return 1;
}

statichere PyTypeObject varlinktype = {
/*  PyObject_HEAD_INIT(&PyType_Type)  Note : This doesn't work on some machines */
  PyObject_HEAD_INIT(0)              
  0,
  "varlink",                          /* Type name    */
  sizeof(swig_varlinkobject),         /* Basic size   */
  0,                                  /* Itemsize     */
  0,                                  /* Deallocator  */ 
  (printfunc) swig_varlink_print,     /* Print        */
  (getattrfunc) swig_varlink_getattr, /* get attr     */
  (setattrfunc) swig_varlink_setattr, /* Set attr     */
  0,                                  /* tp_compare   */
  (reprfunc) swig_varlink_repr,       /* tp_repr      */    
  0,                                  /* tp_as_number */
  0,                                  /* tp_as_mapping*/
  0,                                  /* tp_hash      */
};

/* Create a variable linking object for use later */

SWIGSTATICRUNTIME(PyObject *)
SWIG_newvarlink(void)
{
  swig_varlinkobject *result = 0;
  result = PyMem_NEW(swig_varlinkobject,1);
  varlinktype.ob_type = &PyType_Type;    /* Patch varlinktype into a PyType */
  result->ob_type = &varlinktype;
  /*  _Py_NewReference(result);  Does not seem to be necessary */
  result->nvars = 0;
  result->maxvars = 64;
  result->vars = (swig_globalvar **) malloc(64*sizeof(swig_globalvar *));
  result->vars[0] = 0;
  result->ob_refcnt = 0;
  Py_XINCREF((PyObject *) result);
  return ((PyObject*) result);
}

SWIGSTATICRUNTIME(void)
SWIG_addvarlink(PyObject *p, char *name,
	   PyObject *(*get_attr)(void), int (*set_attr)(PyObject *p))
{
  swig_varlinkobject *v;
  v= (swig_varlinkobject *) p;
	
  if (v->nvars >= v->maxvars -1) {
    v->maxvars = 2*v->maxvars;
    v->vars = (swig_globalvar **) realloc(v->vars,v->maxvars*sizeof(swig_globalvar *));
    if (v->vars == NULL) {
      fprintf(stderr,"SWIG : Fatal error in initializing Python module.\n");
      exit(1);
    }
  }
  v->vars[v->nvars] = (swig_globalvar *) malloc(sizeof(swig_globalvar));
  v->vars[v->nvars]->name = (char *) malloc(strlen(name)+1);
  strcpy(v->vars[v->nvars]->name,name);
  v->vars[v->nvars]->get_attr = get_attr;
  v->vars[v->nvars]->set_attr = set_attr;
  v->nvars++;
  v->vars[v->nvars] = 0;
}

/* -----------------------------------------------------------------------------
 * Pointer type-checking
 * ----------------------------------------------------------------------------- */

/* SWIG pointer structure */
typedef struct SwigPtrType {
  char               *name;               /* Datatype name                  */
  int                 len;                /* Length (used for optimization) */
  void               *(*cast)(void *);    /* Pointer casting function       */
  struct SwigPtrType *next;               /* Linked list pointer            */
} SwigPtrType;

/* Pointer cache structure */
typedef struct {
  int                 stat;               /* Status (valid) bit             */
  SwigPtrType        *tp;                 /* Pointer to type structure      */
  char                name[256];          /* Given datatype name            */
  char                mapped[256];        /* Equivalent name                */
} SwigCacheType;

static int SwigPtrMax  = 64;           /* Max entries that can be currently held */
static int SwigPtrN    = 0;            /* Current number of entries              */
static int SwigPtrSort = 0;            /* Status flag indicating sort            */
static int SwigStart[256];             /* Starting positions of types            */
static SwigPtrType *SwigPtrTable = 0;  /* Table containing pointer equivalences  */

/* Cached values */
#define SWIG_CACHESIZE  8
#define SWIG_CACHEMASK  0x7
static SwigCacheType SwigCache[SWIG_CACHESIZE];  
static int SwigCacheIndex = 0;
static int SwigLastCache = 0;

/* Sort comparison function */
static int swigsort(const void *data1, const void *data2) {
	SwigPtrType *d1 = (SwigPtrType *) data1;
	SwigPtrType *d2 = (SwigPtrType *) data2;
	return strcmp(d1->name,d2->name);
}

/* Register a new datatype with the type-checker */
SWIGSTATICRUNTIME(void) 
SWIG_RegisterMapping(char *origtype, char *newtype, void *(*cast)(void *)) {
  int i;
  SwigPtrType *t = 0,*t1;

  /* Allocate the pointer table if necessary */
  if (!SwigPtrTable) {     
    SwigPtrTable = (SwigPtrType *) malloc(SwigPtrMax*sizeof(SwigPtrType));
  }

  /* Grow the table */
  if (SwigPtrN >= SwigPtrMax) {
    SwigPtrMax = 2*SwigPtrMax;
    SwigPtrTable = (SwigPtrType *) realloc((char *) SwigPtrTable,SwigPtrMax*sizeof(SwigPtrType));
  }
  for (i = 0; i < SwigPtrN; i++) {
    if (strcmp(SwigPtrTable[i].name,origtype) == 0) {
      t = &SwigPtrTable[i];
      break;
    }
  }
  if (!t) {
    t = &SwigPtrTable[SwigPtrN++];
    t->name = origtype;
    t->len = strlen(t->name);
    t->cast = 0;
    t->next = 0;
  }

  /* Check for existing entries */
  while (t->next) {
    if ((strcmp(t->name,newtype) == 0)) {
      if (cast) t->cast = cast;
      return;
    }
    t = t->next;
  }
  t1 = (SwigPtrType *) malloc(sizeof(SwigPtrType));
  t1->name = newtype;
  t1->len = strlen(t1->name);
  t1->cast = cast;
  t1->next = 0;            
  t->next = t1;           
  SwigPtrSort = 0;
}

/* Make a pointer value string */
SWIGSTATICRUNTIME(void) 
SWIG_MakePtr(char *c, const void *ptr, char *type) {
  static char hex[17] = "0123456789abcdef";
  unsigned long p, s;
  char result[24], *r; 
  r = result;
  p = (unsigned long) ptr;
  if (p > 0) {
    while (p > 0) {
      s = p & 0xf;
      *(r++) = hex[s];
      p = p >> 4;
    }
    *r = '_';
    while (r >= result)
      *(c++) = *(r--);
    strcpy (c, type);
  } else {
    strcpy (c, "NULL");
  }
}

/* Function for getting a pointer value */
SWIGSTATICRUNTIME(char *) 
SWIG_GetPtr(char *c, void **ptr, char *t)
{
  unsigned long p;
  char temp_type[256], *name;
  int  i, len, start, end;
  SwigPtrType *sp,*tp;
  SwigCacheType *cache;
  register int d;

  p = 0;
  /* Pointer values must start with leading underscore */
  if (*c != '_') {
    *ptr = (void *) 0;
    if (strcmp(c,"NULL") == 0) return (char *) 0;
    else c;
  }
  c++;
  /* Extract hex value from pointer */
  while (d = *c) {
    if ((d >= '0') && (d <= '9'))
      p = (p << 4) + (d - '0');
    else if ((d >= 'a') && (d <= 'f'))
      p = (p << 4) + (d - ('a'-10));
    else
      break; 
    c++;
  }
  *ptr = (void *) p;
  if ((!t) || (strcmp(t,c)==0)) return (char *) 0;

  if (!SwigPtrSort) {
    qsort((void *) SwigPtrTable, SwigPtrN, sizeof(SwigPtrType), swigsort); 
    for (i = 0; i < 256; i++) SwigStart[i] = SwigPtrN;
    for (i = SwigPtrN-1; i >= 0; i--) SwigStart[(int) (SwigPtrTable[i].name[1])] = i;
    for (i = 255; i >= 1; i--) {
      if (SwigStart[i-1] > SwigStart[i])
	SwigStart[i-1] = SwigStart[i];
    }
    SwigPtrSort = 1;
    for (i = 0; i < SWIG_CACHESIZE; i++) SwigCache[i].stat = 0;
  }
  /* First check cache for matches.  Uses last cache value as starting point */
  cache = &SwigCache[SwigLastCache];
  for (i = 0; i < SWIG_CACHESIZE; i++) {
    if (cache->stat && (strcmp(t,cache->name) == 0) && (strcmp(c,cache->mapped) == 0)) {
      cache->stat++;
      if (cache->tp->cast) *ptr = (*(cache->tp->cast))(*ptr);
      return (char *) 0;
    }
    SwigLastCache = (SwigLastCache+1) & SWIG_CACHEMASK;
    if (!SwigLastCache) cache = SwigCache;
    else cache++;
  }
  /* Type mismatch.  Look through type-mapping table */
  start = SwigStart[(int) t[1]];
  end = SwigStart[(int) t[1]+1];
  sp = &SwigPtrTable[start];

  /* Try to find a match */
  while (start <= end) {
    if (strncmp(t,sp->name,sp->len) == 0) {
      name = sp->name;
      len = sp->len;
      tp = sp->next;
      /* Try to find entry for our given datatype */
      while(tp) {
	if (tp->len >= 255) {
	  return c;
	}
	strcpy(temp_type,tp->name);
	strncat(temp_type,t+len,255-tp->len);
	if (strcmp(c,temp_type) == 0) {
	  strcpy(SwigCache[SwigCacheIndex].mapped,c);
	  strcpy(SwigCache[SwigCacheIndex].name,t);
	  SwigCache[SwigCacheIndex].stat = 1;
	  SwigCache[SwigCacheIndex].tp = tp;
	  SwigCacheIndex = SwigCacheIndex & SWIG_CACHEMASK;
	  /* Get pointer value */
	  *ptr = (void *) p;
	  if (tp->cast) *ptr = (*(tp->cast))(*ptr);
	  return (char *) 0;
	}
	tp = tp->next;
      }
    }
    sp++;
    start++;
  }
  return c;
} 

/* New object-based GetPointer function. This uses the Python abstract
 * object interface to automatically dereference the 'this' attribute
 * of shadow objects. */

SWIGSTATICRUNTIME(char *)
SWIG_GetPtrObj(PyObject *obj, void **ptr, char *type) {
  PyObject *sobj = obj;
  char     *str;
  if (!PyString_Check(obj)) {
    sobj = PyObject_GetAttrString(obj,"this");
    if (!sobj) return "";
  }
  str = PyString_AsString(sobj);
  return SWIG_GetPtr(str,ptr,type);
}

#ifdef __cplusplus
}
#endif


#define SWIG_init    initole

#define SWIG_name    "ole"

#include "ms-ole.h"
#include "ms-ole-summary.h"
#ifdef __cplusplus
extern "C" {
#endif
static PyObject *_wrap__ms_ole_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _result;
    char * _arg0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"s:_ms_ole_open",&_arg0)) 
        return NULL;
    _result = (MsOle *)_ms_ole_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOle_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_open_body(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _result;
    char * _arg0;
    int  _arg1;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"s#:_ms_ole_open_body",&_arg0,&_arg1)) 
        return NULL;
    _result = (MsOle *)_ms_ole_open_body(_arg0,_arg1);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOle_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_destroy(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOle * _arg0;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_destroy",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_destroy. Expected _MsOle_p.");
        return NULL;
        }
    }
    _ms_ole_destroy(_arg0);
    Py_INCREF(Py_None);
    _resultobj = Py_None;
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _result;
    MsOle * _arg0;
    PyObject * _argo0 = 0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_summary_open",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_open. Expected _MsOle_p.");
        return NULL;
        }
    }
    _result = (MsOleSummary *)_ms_ole_summary_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOleSummary_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_close(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _arg0;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_summary_close",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_close. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _ms_ole_summary_close(_arg0);
    Py_INCREF(Py_None);
    _resultobj = Py_None;
    return _resultobj;
}

static PyObject *_wrap__ms_ole_docsummary_open(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    MsOleSummary * _result;
    MsOle * _arg0;
    PyObject * _argo0 = 0;
    char _ptemp[128];

    self = self;
    if(!PyArg_ParseTuple(args,"O:_ms_ole_docsummary_open",&_argo0)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOle_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_docsummary_open. Expected _MsOle_p.");
        return NULL;
        }
    }
    _result = (MsOleSummary *)_ms_ole_docsummary_open(_arg0);
    if (_result) {
        SWIG_MakePtr(_ptemp, (char *) _result,"_MsOleSummary_p");
        _resultobj = Py_BuildValue("s",_ptemp);
    } else {
        Py_INCREF(Py_None);
        _resultobj = Py_None;
    }
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_string(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    char * _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_string",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_string. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (char *)_ms_ole_summary_get_string(_arg0,_arg1);
    _resultobj = Py_BuildValue("s", _result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_long(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    int  _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_long",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_long. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (int )_ms_ole_summary_get_long(_arg0,_arg1);
    _resultobj = Py_BuildValue("i",_result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_short(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    short  _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_short",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_short. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (short )_ms_ole_summary_get_short(_arg0,_arg1);
    _resultobj = Py_BuildValue("h",_result);
    return _resultobj;
}

static PyObject *_wrap__ms_ole_summary_get_time(PyObject *self, PyObject *args) {
    PyObject * _resultobj;
    char * _result;
    MsOleSummary * _arg0;
    int  _arg1;
    PyObject * _argo0 = 0;

    self = self;
    if(!PyArg_ParseTuple(args,"Oi:_ms_ole_summary_get_time",&_argo0,&_arg1)) 
        return NULL;
    if (_argo0) {
        if (_argo0 == Py_None) { _arg0 = NULL; }
        else if (SWIG_GetPtrObj(_argo0,(void **) &_arg0,"_MsOleSummary_p")) {
            PyErr_SetString(PyExc_TypeError,"Type error in argument 1 of _ms_ole_summary_get_time. Expected _MsOleSummary_p.");
        return NULL;
        }
    }
    _result = (char *)_ms_ole_summary_get_time(_arg0,_arg1);
    _resultobj = Py_BuildValue("s", _result);
    return _resultobj;
}

static PyMethodDef oleMethods[] = {
	 { "_ms_ole_summary_get_time", _wrap__ms_ole_summary_get_time, METH_VARARGS },
	 { "_ms_ole_summary_get_short", _wrap__ms_ole_summary_get_short, METH_VARARGS },
	 { "_ms_ole_summary_get_long", _wrap__ms_ole_summary_get_long, METH_VARARGS },
	 { "_ms_ole_summary_get_string", _wrap__ms_ole_summary_get_string, METH_VARARGS },
	 { "_ms_ole_docsummary_open", _wrap__ms_ole_docsummary_open, METH_VARARGS },
	 { "_ms_ole_summary_close", _wrap__ms_ole_summary_close, METH_VARARGS },
	 { "_ms_ole_summary_open", _wrap__ms_ole_summary_open, METH_VARARGS },
	 { "_ms_ole_destroy", _wrap__ms_ole_destroy, METH_VARARGS },
	 { "_ms_ole_open_body", _wrap__ms_ole_open_body, METH_VARARGS },
	 { "_ms_ole_open", _wrap__ms_ole_open, METH_VARARGS },
	 { NULL, NULL }
};
#ifdef __cplusplus
}
#endif
/*
 * This table is used by the pointer type-checker
 */
static struct { char *n1; char *n2; void *(*pcnv)(void *); } _swig_mapping[] = {
    { "_signed_long","_long",0},
    { "_long","_unsigned_long",0},
    { "_long","_signed_long",0},
    { "_unsigned_long","_long",0},
    { "_signed_int","_int",0},
    { "_unsigned_short","_short",0},
    { "_signed_short","_short",0},
    { "_unsigned_int","_int",0},
    { "_short","_unsigned_short",0},
    { "_short","_signed_short",0},
    { "_int","_unsigned_int",0},
    { "_int","_signed_int",0},
{0,0,0}};

static PyObject *SWIG_globals;
#ifdef __cplusplus
extern "C" 
#endif
SWIGEXPORT(void) initole() {
	 PyObject *m, *d;
	 SWIG_globals = SWIG_newvarlink();
	 m = Py_InitModule("ole", oleMethods);
	 d = PyModule_GetDict(m);
{
   int i;
   for (i = 0; _swig_mapping[i].n1; i++)
        SWIG_RegisterMapping(_swig_mapping[i].n1,_swig_mapping[i].n2,_swig_mapping[i].pcnv);
}
}

--- Added File setup.py in package CMF ---
#!/usr/bin/env python

from distutils.core import setup, Extension

setup(name = "ole",
      version = "1.0", 
      description = "libole2 Interface",
      author = "John Platten",
      author_email = "jplatten@digicool.com",
      ext_modules =
          [('ole',
          { 'sources':
             ['ole_wrap.c',
              'ole.c',
              'version.c',
              'ms-ole.c',
              'ms-ole-vba.c',
              'ms-ole-summary.c'],
            'include_dirs': ['.'],
            'includes':
             ['config.h',
              'glibconfig.h',
              'libole2.h',
              'ms-ole.h',
              'ms-ole-vba.h',
              'ms-ole-summary.h'],
            'libraries': ["glib"],
          }
          )]

      )

--- Added File test-ole.c in package CMF ---
/**
 * test-ole.c: OLE2 file format helper program,
 *             good for dumping OLE streams, and
 * corresponding biff records, and hopefuly
 * some more ...
 *
 * Author:
 *    Michael Meeks (michael@imaginator.com)
 **/

#define TEST_DEBUG 0

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
/* For ctime() */
#include <time.h>

#include <ms-ole.h>
#include <ms-ole-vba.h>
#include <ms-ole-summary.h>

#define BIFF_TYPES_FILE    "biff-types.h"

#if TEST_DEBUG > 0
static char delim[]=":";
#else
static char delim[]=" ";
#endif
static char **arg_data = NULL;
static int    arg_cur  = 0;

typedef struct {
	guint16 opcode;
	char *name;
} GENERIC_TYPE;

static GPtrArray *biff_types   = NULL;

static char *cur_dir = NULL;

static void
read_types (char *fname, GPtrArray **types)
{
	FILE *file = fopen(fname, "r");
	char buffer[1024];
	*types = g_ptr_array_new ();
	if (!file) {
		char *newname = g_strconcat ("../", fname, NULL);
		file = fopen (newname, "r");
	}
	if (!file) {
		printf ("Can't find vital file '%s'\n", fname);
		return;
	}
	while (!feof(file)) {
		char *p;
		fgets(buffer,1023,file);
		for (p=buffer;*p;p++)
			if (*p=='0' && *(p+1)=='x') {
				GENERIC_TYPE *bt = g_new (GENERIC_TYPE,1);
				char *name, *pt;
				bt->opcode=strtol(p+2,0,16);
				pt = buffer;
				while (*pt && *pt != '#') pt++;      /* # */
				while (*pt && !isspace(*pt)) pt++;  /* define */
				while (*pt &&  isspace(*pt)) pt++;  /* '   ' */
				while (*pt && *pt != '_') pt++;     /* BIFF_ */
				name = *pt?pt+1:pt;
				while (*pt && !isspace(*pt)) pt++;
				bt->name=g_strndup(name, (pt-name));
				g_ptr_array_add (*types, bt);
				break;
			}
	}
	fclose (file);
}

static char*
get_biff_opcode_name (guint16 opcode)
{
	int lp;
	if (!biff_types)
		read_types (BIFF_TYPES_FILE, &biff_types);
	/* Count backwars to give preference to non-filtered record types */
	for (lp=biff_types->len; --lp >= 0 ;) {
		GENERIC_TYPE *bt = g_ptr_array_index (biff_types, lp);
		if (bt->opcode>0xff) {
			if (bt->opcode == opcode)
				return bt->name;
		} else {
			if (bt->opcode == (opcode&0xff))
				return bt->name;
		}
	}
	return "Unknown";
}

static void
list_files (MsOle *ole)
{
	char     **names;
	MsOleErr   result;
	int        lp;

	result = ms_ole_directory (&names, ole, cur_dir);
	if (result != MS_OLE_ERR_OK) {
		g_warning ("Failed dir");
		return;
	}

	if (!names[0])
		printf ("Empty directory\n");

	for (lp = 0; names[lp]; lp++) {
		MsOleStat s;
		result = ms_ole_stat (&s, ole, cur_dir, names[lp]);

		if (s.type == MsOleStreamT)
			if (names[lp][0] < 0x30) {
				printf ("'\\%x%s' : length %d bytes\n",
					names[lp][0], names[lp] + 1, s.size);
			} else {
				printf ("'%s : length %d bytes\n",
					names[lp], s.size);
			}
		else
			if (names[lp][0] < 0x30) {
				printf ("'\\%d[%s]' : Storage ( directory )\n",
					names[lp][0], names [lp] + 1);
			} else {
				printf ("'[%s] : Storage ( directory )\n",
					names [lp]);
			}
		
	}
}

static void
list_commands ()
{
	printf ("command can be one or all of:\n");
	printf (" * ls:                   list files\n");
	printf (" * cd:                   enter storage\n");
	printf (" * biff    <stream name>:   dump biff records, merging continues\n");
	printf (" * biffraw <stream name>:   dump biff records no merge + raw data\n");
	printf (" * dump    <stream name>:   dump stream\n");
	printf (" * summary              :   dump summary info\n");
	printf (" * docsummary           :   dump document summary info\n");
	printf (" * debug                :   dump internal ole library status\n");
	printf (" * tree                 :   dump the tree\n");
	printf (" * vba                  :   attempt to dump vba \n");
	printf (" Raw transfer commands\n");
	printf (" * get     <stream name> <fname>\n");
	printf (" * put     <fname> <stream name>\n");
	printf (" * copyin  [<fname>,]...\n");
	printf (" * copyout [<fname>,]...\n");
	printf (" * quit,exit,bye:        exit\n");
}

static void
syntax_error (char *err)
{
	if (err) {
		printf("Error: '%s'\n",err);
		exit(1);
	}
		
	printf ("Sytax:\n");
	printf (" ole <ole-file> [-i] [commands...]\n\n");
	printf (" -i: Interactive, queries for fresh commands\n\n");

	list_commands ();
	exit(1);
}

/* ---------------------------- End cut ---------------------------- */

static gboolean
simple_regexp (const char *regexp, const char *fname)
{
	int      i;
	gboolean ret = TRUE;

	g_return_val_if_fail (fname != NULL, FALSE);
	g_return_val_if_fail (regexp != NULL, FALSE);

	for (i = 0; regexp [i] && fname [i]; i++) {
		if (regexp [i] == '.')
			continue;

		if (toupper (regexp [i]) != toupper (fname [i])) {
			ret = FALSE;
			break;
		}
	}

	if (regexp [i] && regexp [i] == '*')
		ret = TRUE;

	else if (!regexp [i] && fname [i])
		ret = FALSE;

/*	if (ret)
	printf ("'%s' matched '%s'\n", regexp, fname);*/

	return ret;
}

/*
 * This should take a path to check the directory out there.
 */ 
static char *
get_regexp_name (const char *regexp, const char *path, MsOle *ole)
{
	char      *res = NULL;
	char     **names;
	MsOleErr   result;
	int        lp;

	result = ms_ole_directory (&names, ole, path);
	if (result != MS_OLE_ERR_OK) {
		g_warning ("Failed dir");
		return NULL;
	}

	if (!names [0])
		printf ("Empty directory\n");

	for (lp = 0; names[lp]; lp++) {
		if (simple_regexp (regexp, names [lp])) {
			res = g_strdup (names [lp]);
			break;
		}
	}

	return res;
}

static void
enter_dir (MsOle *ole)
{
	char *newpath, *ptr, *p;

	p = arg_data [arg_cur++];

	if (!p) {
		printf ("Takes a directory argument\n");
		return;
	}

	if (!g_strcasecmp (p, "..")) {
		guint lp;
		char **tmp;
		GString *newp = g_string_new ("");

		tmp = g_strsplit (cur_dir, "/", -1);
		lp  = 0;
		if (!tmp[lp])
			return;

		while (tmp[lp+1]) {
			g_string_sprintfa (newp, "%s/", tmp[lp]);
			lp++;
		}
		g_free (cur_dir);
		cur_dir = newp->str;
		g_string_free (newp, FALSE);
	} else {
		MsOleStat s;
		MsOleErr  result;

		ptr = get_regexp_name (p, cur_dir, ole);
		if (!ptr)
			return;

		newpath = g_strconcat (cur_dir, ptr, "/", NULL);

		result = ms_ole_stat (&s, ole, newpath, "");
		if (result == MS_OLE_ERR_EXIST) {
			printf ("Storage '%s' not found\n", ptr);
			g_free (newpath);
			return;
		}
		if (result != MS_OLE_ERR_OK) {
			g_warning ("internal error");
			g_free (newpath);
			return;
		}
		if (s.type == MsOleStreamT) {
			printf ("Trying to enter a stream. (%d)\n", s.type);
			g_free (newpath);
			return;
		}

		g_free (cur_dir);
		cur_dir = newpath;
	}
}

static MsOleErr
test_stream_open (MsOleStream ** const stream, MsOle *f,
		  const char *path, const char *fname, char mode)
{
	MsOleErr err;
	char    *name;

	name = get_regexp_name (fname, path, f);
	if (name) {
		err = ms_ole_stream_open (stream, f, path, name, mode);
		g_free (name);
	} else /* Fall back to original */
		err = ms_ole_stream_open (stream, f, fname, name, mode);

	return err;
}


static void
do_dump (MsOle *ole)
{
	char        *ptr;
	MsOleStream *stream;
	guint8      *buffer;
	gchar	    *tname;

	ptr = arg_data [arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}

	tname = g_strdup (ptr);
	if (strcmp(tname, "SummaryInformation") == 0) {
	        printf ("Changing name to prepend 005\n");
		tname = "\05SummaryInformation";
	}
	if (strcmp(tname, "DocumentSummaryInformation") == 0) {
	        printf ("Changing name to prepend 005\n");
		tname = "\05DocumentSummaryInformation";
	}
	
	if (test_stream_open (&stream, ole, cur_dir, tname, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	buffer = g_malloc (stream->size);
	stream->read_copy (stream, buffer, stream->size);
	printf ("Stream : '%s' length 0x%x\n", ptr, stream->size);
	if (buffer)
		ms_ole_dump (buffer, stream->size);
	else
		printf ("Failed read\n");
	ms_ole_stream_close (&stream);
}

/* 
 * This is a massively cut down version ...
 */
typedef struct {
	guint16  opcode;
	guint32  length;        /* NB. can be extended by a continue opcode */
	guint8  *data;
	guint32  streamPos;
	MsOleStream *pos;
} BiffQuery;

static BiffQuery *
ms_biff_query_new (MsOleStream *ptr)
{
	BiffQuery *bq   ;
	if (!ptr)
		return 0;
	bq = g_new0 (BiffQuery, 1);
	bq->opcode = 0;
	bq->length = 0;
	bq->pos    = ptr;
	return bq;
}

static int
ms_biff_query_next (BiffQuery *bq)
{
	guint8  tmp[4];
	int ans=1;

	if (!bq || bq->pos->position >= bq->pos->size)
		return 0;
	if (bq->data)
		g_free (bq->data);

	bq->streamPos = bq->pos->position;
	if (!bq->pos->read_copy (bq->pos, tmp, 4))
		return 0;
	bq->opcode = MS_OLE_GET_GUINT16 (tmp);
	bq->length = MS_OLE_GET_GUINT16 (tmp+2);

	if (bq->length > 0) {
		bq->data = g_new0 (guint8, bq->length);
		if (!bq->pos->read_copy(bq->pos, bq->data, bq->length)) {
			ans = 0;
			g_free (bq->data);
			bq->length = 0;
		}
	}

	if (!bq->length) {
		bq->data = NULL;
		return 1;
	}

	return (ans);
}

static void
do_biff (MsOle *ole)
{
	char *ptr;
	MsOleStream *stream;
	
	ptr = arg_data[arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}
       
	if (test_stream_open (&stream, ole, cur_dir, ptr, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	{
		BiffQuery *q = ms_biff_query_new (stream);
		guint16 last_opcode=0xffff;
		guint32 last_length=0;
		guint32 count=0;
		while (ms_biff_query_next(q)) {
			if (q->opcode == last_opcode &&
			    q->length == last_length)
				count++;
			else {
				if (count>0)
					printf (" x %d\n", count+1);
				else
					printf ("\n");
				count=0;
				printf ("Opcode 0x%3x : %15s, length %d",
					q->opcode, get_biff_opcode_name (q->opcode), q->length);
			}
			last_opcode=q->opcode;
			last_length=q->length;
		}
		printf ("\n");
		ms_ole_stream_close (&stream);
	}
}

static void
do_biff_raw (MsOle *ole)
{
	char *ptr;
	MsOleStream *stream;
	
	ptr = arg_data[arg_cur++];
	if (!ptr) {
		printf ("Need a stream name\n");
		return;
	}
       
	if (test_stream_open (&stream, ole, cur_dir, ptr, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", ptr);
		return;
	}
	{
		guint8 data[4], *buffer;
		
		buffer = g_new (guint8, 65550);
		while (stream->read_copy (stream, data, 4)) {
			guint32 len=MS_OLE_GET_GUINT16(data+2);
/*			printf ("0x%4x Opcode 0x%3x : %15s, length 0x%x (=%d)\n", stream->position,
				MS_OLE_GET_GUINT16(data), get_biff_opcode_name (MS_OLE_GET_GUINT16(data)),
				len, len);*/
			printf ("Opcode 0x%3x : %15s, length 0x%x (=%d)\n",
				MS_OLE_GET_GUINT16(data), get_biff_opcode_name (MS_OLE_GET_GUINT16(data)),
				len, len);
			stream->read_copy (stream, buffer, len);
			ms_ole_dump (buffer, len);
			buffer[0]=0;
			buffer[len-1]=0;
		}
		ms_ole_stream_close (&stream);
	}
}

static void
really_get (MsOle *ole, char *from, char *to)
{
	MsOleStream *stream;
	
	if (test_stream_open (&stream, ole, cur_dir, from, 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", from);
		return;
	} else {
		guint8 *buffer = g_malloc (stream->size);
		FILE *f = fopen (to, "w");
		stream->read_copy (stream, buffer, stream->size);
		printf ("Stream : '%s' length 0x%x\n", from, stream->size);
		if (f && buffer) {
			fwrite (buffer, 1, stream->size, f);
			fclose (f);
		} else
			printf ("Failed write to '%s'\n", to);
		ms_ole_stream_close (&stream);

	}
}

static void
do_get (MsOle *ole)
{
	char *from, *to;

	from = arg_data[arg_cur++];
	if (!from)
		to = NULL;
	else
		to = arg_data[arg_cur++];
	really_get (ole, from, to);
}

static void
really_put (MsOle *ole, char *from, char *to)
{
	MsOleStream *stream;
	char buffer[8200];

	if (!from || !to) {
		printf ("Null name\n");
		return;
	}

	if (test_stream_open (&stream, ole, cur_dir, to, 'w') !=
	    MS_OLE_ERR_OK) {
		printf ("Error opening '%s'\n", to);
		return;
	} else {
		FILE *f = fopen (from, "r");
		size_t len;
		int block=0;

		if (!f || !stream) {
			printf ("Failed write\n");
			return;
		}

		stream->lseek (stream, 0, MsOleSeekSet);
	       
		do {
			guint32 lenr = 1+ (int)(8192.0*rand()/(RAND_MAX+1.0));
			len = fread (buffer, 1, lenr, f);
			printf ("Transfering block %d = %d bytes\n", block++, len); 
			stream->write (stream, buffer, len);
		} while (!feof(f) && len>0);

		fclose (f);
		ms_ole_stream_close (&stream);
	}
}

static void
do_summary (MsOle *ole)
{
	MsOleSummary        *si;
	MsOleSummaryPreview  preview;
	gboolean             ok;
	gchar               *txt;
	guint32              num;
	GTimeVal             timeval;

	si = ms_ole_summary_open (ole);
	if (!si) {
		printf ("No summary information\n");
		return;
	}

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_TITLE, &ok);
	if (ok)
		printf ("The title is %s\n", txt);
	else
		printf ("no title found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_SUBJECT, &ok);
	if (ok)
		printf ("The subject is %s\n", txt);
	else
		printf ("no subject found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_AUTHOR, &ok);
	if (ok)
		printf ("The author is %s\n", txt);
	else
		printf ("no author found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_KEYWORDS, &ok);
	if (ok)
		printf ("The keywords are %s\n", txt);
	else
		printf ("no keywords found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_COMMENTS, &ok);
	if (ok)
		printf ("The comments are %s\n", txt);
	else
		printf ("no comments found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_TEMPLATE, &ok);
	if (ok)
		printf ("The template was %s\n", txt);
	else
		printf ("no template found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_LASTAUTHOR, &ok);
	if (ok)
		printf ("The last author was %s\n", txt);
	else
		printf ("no last author found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_REVNUMBER, &ok);
	if (ok)
		printf ("The rev no was %s\n", txt);
	else
		printf ("no rev no found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_APPNAME, &ok);
	if (ok)
		printf ("The app name was %s\n", txt);
	else
		printf ("no app name found\n");
	g_free (txt);

	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_TOTAL_EDITTIME, &ok);
	if (ok)
		printf ("Total edit time is %ld", timeval.tv_sec);
	else
		printf ("no total edit time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_LASTPRINTED, &ok);
	if (ok)
		printf ("Last printed at %s", ctime (&timeval.tv_sec));
	else
		printf ("no last printed time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_CREATED, &ok);
	if (ok)
		printf ("Was created at %s", ctime (&timeval.tv_sec));
	else
		printf ("no creation time found\n");
	
	timeval = ms_ole_summary_get_time (si, MS_OLE_SUMMARY_LASTSAVED, &ok);
	if (ok)
		printf ("Last saved at %s", ctime (&timeval.tv_sec));
	else
		printf ("no last saved time found\n");
	
	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_PAGECOUNT, &ok);
	if (ok)
		printf ("PageCount is %d\n", num);
	else
		printf ("no pagecount found\n");

	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_WORDCOUNT, &ok);
	if (ok)
		printf ("WordCount is %d\n", num);
	else
		printf ("no wordcount found\n");

	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_CHARCOUNT, &ok);
	if (ok)
		printf ("CharCount is %d\n", num);
	else
		printf ("no charcount found\n");
	
	num = ms_ole_summary_get_long (si, MS_OLE_SUMMARY_SECURITY, &ok);
	if (ok)
		printf ("Security is %d\n", num);
	else
		printf ("no security found\n");

	num = ms_ole_summary_get_short (si, MS_OLE_SUMMARY_CODEPAGE, &ok);
	if (ok)
		printf ("CodePage is %d\n", num);
	else
		printf ("no codepage found\n");

	preview = ms_ole_summary_get_preview (si, MS_OLE_SUMMARY_THUMBNAIL, &ok);
	if (ok) {
		printf ("preview is %d bytes long\n", preview.len);
		ms_ole_summary_preview_destroy (preview);
	} else
		printf ("no preview found\n");

	ms_ole_summary_close (si);
}

static void
do_docsummary (MsOle *ole)
{
	MsOleSummary        *si;
	gboolean             ok;
	gchar               *txt;

	si = ms_ole_docsummary_open (ole);
	if (!si) {
		printf ("No document summary information\n");
		return;
	}

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_CATEGORY, &ok);
	if (ok)
		printf ("The category is %s\n", txt);
	else
		printf ("no category found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_PRESFORMAT, &ok);
	if (ok)
		printf ("The presformat is %s\n", txt);
	else
		printf ("no presformat found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_MANAGER, &ok);
	if (ok)
		printf ("The manager is %s\n", txt);
	else
		printf ("no manager found\n");
	g_free (txt);

	txt = ms_ole_summary_get_string (si, MS_OLE_SUMMARY_COMPANY, &ok);
	if (ok)
		printf ("The company is %s\n", txt);
	else
		printf ("no company found\n");
	g_free (txt);

	ms_ole_summary_close (si);
}

static void
do_put (MsOle *ole)
{
	char *from, *to;

	from = arg_data[arg_cur++];
	if (!from)
		to = NULL;
	else
		to = arg_data[arg_cur++];

	if (!from || !to) {
		printf ("put <filename> <stream>\n");
		return;
	}

	really_put (ole, from, to);
}

static void
do_copyin (MsOle *ole)
{
	char *from;

	do {
		from = arg_data[arg_cur++];
		if (from)
			really_put (ole, from, from);
	} while (from);
}

static void
do_copyout (MsOle *ole)
{
	char *from;

	do {
		from = arg_data[arg_cur++];
		if (from)
			really_get (ole, from, from);
	} while (from);
}

static void
dump_vba (MsOle *f)
{
	const char	*vbapath = "/_VBA_PROJECT_CUR";
	char		**dir;
	char		*txt;

	int		 i;
	int		 module_count;

	MsOleStream	*s;
	MsOleStat	 st;

	if (ms_ole_stat (&st, f, vbapath, "") != MS_OLE_ERR_OK ||
	    st.type == MsOleStreamT) {
		printf ("No valid VBA found\n");
		return;
	}

	if (test_stream_open (&s, f, vbapath, "PROJECT", 'r') !=
	    MS_OLE_ERR_OK) {
		printf ("No project file... wierd\n");
	} else {
		txt = g_new (guint8, s->size);
		if (!s->read_copy (s, txt, s->size))
			printf ("Failed to read project stream\n");
		else {
			printf ("----------\n");
			printf ("Project file:\n");
			printf ("%s", txt);
			printf ("----------\n");
		}
		ms_ole_stream_close (&s);
		g_free (txt);
	}

	txt = g_strconcat (vbapath, "/VBA", NULL);
	if (ms_ole_directory (&dir, f, txt) != MS_OLE_ERR_OK) {
		printf ("No VBA subdirectory found");
		g_free (txt);
		return;
	}

	module_count = 0;
	for (i = 0; dir [i]; i++) {
		if (test_stream_open (&s, f, txt, dir[i], 'r') !=
		    MS_OLE_ERR_OK)
			printf ("Error opening %s\n", dir [i]);
		else {
			MsOleVba	*vba;

			vba = ms_ole_vba_open (s);
			if (!vba)
				g_warning ("Stream '%s' not VBA", dir [i]);
			else {
				module_count++;

				while (!ms_ole_vba_eof (vba))
					printf ("%c", ms_ole_vba_getc (vba));
			}
			ms_ole_vba_close (vba);

			ms_ole_stream_close (&s);
		}
	}

	if (!module_count)
		printf ("Strange no modules found\n");

	g_free (txt);
}

int
main_original (int argc, char **argv)
{
	MsOle *ole;
	int lp, exit = 0, interact = 0;
	char *buffer = g_new (char, 1024) ;

	if (argc<2)
		syntax_error(0);

	printf ("Ole file '%s'\n", argv[1]);
	if (ms_ole_open_vfs (&ole, argv[1], TRUE, NULL)
	    != MS_OLE_ERR_OK) {
		printf ("Creating new file '%s'\n", argv[1]);
		if (ms_ole_create_vfs (&ole, argv[1], TRUE, NULL)
		    != MS_OLE_ERR_OK)
			syntax_error ("Can't open file or create new one");
	}

	if (argc<=2)
		syntax_error ("Need command or -i");

	if (argc>2 && argv[argc-1][0]=='-'
	    && argv[argc-1][1]=='i') 
		interact=1;
	else {
		char *str=g_strdup(argv[2]) ;
		for (lp=3;lp<argc;lp++)
			str = g_strconcat(str," ",argv[lp],NULL); /* FIXME Mega leak :-) */
		buffer = str; /* and again */
	}

	cur_dir = g_strdup ("/");

	do
	{
		char *ptr;

		if (interact) {
			fprintf (stdout,"> ");
			fflush (stdout);
			fgets (buffer, 1023, stdin);
		}

		arg_data = g_strsplit (g_strchomp (buffer), delim, -1);
		arg_cur  = 0;
		if (!arg_data && interact) continue;
		if (!interact)
			printf ("Command : '%s'\n", arg_data[0]);
		ptr = arg_data[arg_cur++];
		if (!ptr)
			continue;

		if (g_strcasecmp (ptr, "ls") == 0)
			list_files (ole);
		else if (g_strcasecmp (ptr, "cd") == 0)
			enter_dir (ole);
		else if (g_strcasecmp (ptr, "dump") == 0)
			do_dump (ole);
		else if (g_strcasecmp (ptr, "biff") == 0)
			do_biff (ole);
		else if (g_strcasecmp (ptr, "biffraw") == 0)
			do_biff_raw (ole);
		else if (g_strcasecmp (ptr, "get") == 0)
			do_get (ole);
		else if (g_strcasecmp (ptr, "put") == 0)
			do_put (ole);
		else if (g_strcasecmp (ptr, "copyin") == 0)
			do_copyin (ole);
		else if (g_strcasecmp (ptr, "copyout") == 0)
			do_copyout (ole);
		else if (g_strcasecmp (ptr, "summary") == 0)
			do_summary (ole);
		else if (g_strcasecmp (ptr, "docsummary") == 0)
			do_docsummary (ole);
		else if (g_strcasecmp (ptr, "debug") == 0)
			ms_ole_debug (ole, 1);
		else if (g_strcasecmp (ptr, "tree") == 0)
			ms_ole_debug (ole, 2);
		else if (g_strcasecmp (ptr, "vba") == 0)
			dump_vba (ole);
		else if (g_strcasecmp (ptr,"help") == 0 ||
			 g_strcasecmp (ptr,"?") == 0 ||
			 g_strcasecmp (ptr,"info") == 0 ||
			 g_strcasecmp (ptr,"man") == 0)
			list_commands ();
		else if (g_strcasecmp (ptr,"exit") == 0 ||
			 g_strcasecmp (ptr,"quit") == 0 ||
			 g_strcasecmp (ptr,"q") == 0 ||
			 g_strcasecmp (ptr,"bye") == 0)
			exit = 1;
	}
	while (!exit && interact);

	ms_ole_destroy (&ole);
	return 0;
}

//----------------------------------------------------------------------------//

int main_file (int argc, char **argv)
{
	MsOle *ole;
	// int lp, exit = 0, interact = 0;
	// char *buffer = g_new (char, 1024) ;

	printf("\n");

	
	ms_ole_open_vfs (&ole, argv[1], FALSE, NULL);
	do_summary (ole);
	do_docsummary (ole);
	ms_ole_destroy (&ole);
	
	printf("\n");
	return 0;
}

//----------------------------------------------------------------------------//

#define JP_MAX_BUF_LEN 100000
unsigned char jp_buf_buf[JP_MAX_BUF_LEN+1];

MsOle *jp_msOle;

int main_body (int argc, char **argv)
{
	MsOle *msOle;
	MsOleErr msOleErr;
	int fd;

	unsigned char *jp_buf_ptr;
	size_t jp_buf_len;
	
	fd = open(argv[1], O_RDONLY);
	jp_buf_ptr = jp_buf_buf;
	jp_buf_len = read(fd, jp_buf_ptr, JP_MAX_BUF_LEN);
	close(fd);
		
	msOleErr = ms_ole_open_vfs_body (&jp_msOle, argv[1], FALSE, NULL, jp_buf_ptr, jp_buf_len);
	do_summary(jp_msOle);
	do_docsummary(jp_msOle);
	ms_ole_destroy(&jp_msOle);
}

//----------------------------------------------------------------------------//

int main (int argc, char **argv)
{
	printf("\n");
	
	main_file(argc, argv);
	main_body(argc, argv);
	
	printf("\n");
	return 0;
}

//----------------------------------------------------------------------------//

--- Added File version.c in package CMF ---
int libole2_major_version = 0;
int libole2_minor_version = 1;
int libole2_micro_version = 7;