Description: Updated to current libxls
 Bug report #895564 reveals five CVE reports made public last November;
 these have all been addressed upstream in libxls, see 
 https://github.com/evanmiller/libxls/issues/2 through
 https://github.com/evanmiller/libxls/issues/6.
Author: Dirk Eddelbuettel <edd@debian.org>
Bug-Debian: https://bugs.debian.org/895564

---
Origin: upstream, https://github.com/evanmiller/libxls, various
Bug-Debian: https://bugs.debian.org/895564
Forwarded: not-needed
Last-Update: 2018-04-12

--- r-cran-readxl-1.0.0.orig/src/endian.c
+++ r-cran-readxl-1.0.0/src/endian.c
@@ -37,22 +37,12 @@ int xls_is_bigendian()
 {
 #if defined (__BIG_ENDIAN__)
     return 1;
-#elif defined (_WIN32)
-    return 0;
 #elif defined (__LITTLE_ENDIAN__)
     return 0;
 #else
-// #warning NO ENDIAN
     static int n = 1;
 
-    if (*(char *)&n == 1)
-    {
-        return 0;
-    }
-    else
-    {
-        return 1;
-    }
+    return (*(char *)&n == 0);
 #endif
 }
 
@@ -75,11 +65,11 @@ DWORD xlsIntVal (DWORD i)
 unsigned short xlsShortVal (short s)
 {
     unsigned char c1, c2;
-
+    
     if (xls_is_bigendian()) {
         c1 = s & 255;
         c2 = (s >> 8) & 255;
-
+    
         return (c1 << 8) + c2;
     } else {
         return s;
@@ -113,10 +103,8 @@ void xlsConvertBiff(BIFF *b)
     b->type = xlsShortVal(b->type);
     b->id_make = xlsShortVal(b->id_make);
     b->year = xlsShortVal(b->year);
-    if (b->ver == 0x600) {
-      b->flags = xlsIntVal(b->flags);
-      b->min_ver = xlsIntVal(b->min_ver);
-    }
+    b->flags = xlsIntVal(b->flags);
+    b->min_ver = xlsIntVal(b->min_ver);
 }
 
 void xlsConvertWindow(WIND1 *w)
--- r-cran-readxl-1.0.0.orig/src/libxls/ole.h
+++ r-cran-readxl-1.0.0/src/libxls/ole.h
@@ -37,7 +37,7 @@
 
 #include "libxls/xlstypes.h"
 
-#if defined(_AIX) || defined(__sun)
+#ifdef AIX
 #pragma pack(1)
 #else
 #pragma pack(push, 1)
@@ -86,7 +86,7 @@ typedef	struct st_olefiles
     long count;
     struct st_olefiles_data
     {
-        BYTE*	name;
+        char*	name;
         DWORD	start;
         DWORD	size;
    }
@@ -97,6 +97,10 @@ st_olefiles;
 typedef struct OLE2
 {
     FILE*		file;
+    const void *buffer;
+    size_t      buffer_len;
+    size_t      buffer_pos;
+
     WORD		lsector;
     WORD		lssector;
     DWORD		cfat;
@@ -107,9 +111,16 @@ typedef struct OLE2
     DWORD		csfat;
     DWORD		difstart;
     DWORD		cdif;
+
     DWORD*		SecID;	// regular sector data
+    DWORD       SecIDCount;
+
 	DWORD*		SSecID;	// short sector data
+    DWORD       SSecIDCount;
+
 	BYTE*		SSAT;	// directory of short sectors
+    DWORD       SSATCount;
+
     st_olefiles	files;
 }
 OLE2;
@@ -129,7 +140,7 @@ typedef struct OLE2Stream
 }
 OLE2Stream;
 
-#if defined(_AIX) || defined(__sun)
+#ifdef AIX
 #pragma pack(1)
 #else
 #pragma pack(push, 1)
@@ -137,7 +148,7 @@ OLE2Stream;
 
 typedef struct PSS
 {
-    BYTE	name[64];
+    char	name[64];
     WORD	bsize;
     BYTE	type;		//STGTY
 #define PS_EMPTY		00
@@ -160,14 +171,15 @@ PSS;
 
 #pragma pack(pop)
 
-extern size_t ole2_read(void* buf,size_t size,size_t count,OLE2Stream* olest);
+extern ssize_t ole2_read(void* buf,size_t size,size_t count,OLE2Stream* olest);
 extern OLE2Stream* ole2_sopen(OLE2* ole,DWORD start, size_t size);
-extern void ole2_seek(OLE2Stream* olest,DWORD ofs);
-extern OLE2Stream*  ole2_fopen(OLE2* ole,BYTE* file);
+extern int ole2_seek(OLE2Stream* olest,DWORD ofs);
+extern OLE2Stream*  ole2_fopen(OLE2* ole, const char *file);
 extern void ole2_fclose(OLE2Stream* ole2st);
-extern OLE2* ole2_open(const BYTE *file);
+extern OLE2* ole2_open_file(const char *file);
+extern OLE2* ole2_open_buffer(const void *buffer, size_t len);
 extern void ole2_close(OLE2* ole2);
-extern void ole2_bufread(OLE2Stream* olest);
+extern int ole2_bufread(OLE2Stream* olest);
 
 
 #endif
--- r-cran-readxl-1.0.0.orig/src/libxls/xls.h
+++ r-cran-readxl-1.0.0/src/libxls/xls.h
@@ -41,18 +41,34 @@ extern "C" {
 #include "libxls/xlsstruct.h"
 #include "libxls/xlstool.h"
 
+typedef enum {
+    LIBXLS_OK,
+    LIBXLS_ERROR_OPEN,
+    LIBXLS_ERROR_SEEK,
+    LIBXLS_ERROR_READ,
+    LIBXLS_ERROR_PARSE,
+    LIBXLS_ERROR_MALLOC
+} xls_error_t;
 
 extern const char* xls_getVersion(void);
+extern const char* xls_getError(xls_error_t code);
 
 extern int xls(int debug);	// Set debug. Force library to load?
 extern void xls_set_formula_hander(xls_formula_handler handler);
 
-extern void xls_parseWorkBook(xlsWorkBook* pWB);
-extern void xls_parseWorkSheet(xlsWorkSheet* pWS);
+extern xls_error_t xls_parseWorkBook(xlsWorkBook* pWB);
+extern xls_error_t xls_parseWorkSheet(xlsWorkSheet* pWS);
 
-extern xlsWorkBook* xls_open(const char *file,const char *charset);	// convert 16bit strings within the spread sheet to this 8-bit encoding (UTF-8 default)
-#define xls_close xls_close_WB                  // historical
-extern void xls_close_WB(xlsWorkBook* pWB);     // preferred name
+// Preferred API
+// charset - convert 16bit strings within the spread sheet to this 8-bit encoding (UTF-8 default)
+extern xlsWorkBook *xls_open_file(const char *file, const char *charset, xls_error_t *outError);
+extern xlsWorkBook *xls_open_buffer(const unsigned char *data, size_t data_len,
+        const char *charset, xls_error_t *outError);
+extern void xls_close_WB(xlsWorkBook* pWB);
+
+// Historical API
+extern xlsWorkBook* xls_open(const char *file,const char *charset);
+#define xls_close xls_close_WB
 
 extern xlsWorkSheet * xls_getWorkSheet(xlsWorkBook* pWB,int num);
 extern void xls_close_WS(xlsWorkSheet* pWS);
--- r-cran-readxl-1.0.0.orig/src/libxls/xlsstruct.h
+++ r-cran-readxl-1.0.0/src/libxls/xlsstruct.h
@@ -80,7 +80,7 @@
 
 #define BLANK_CELL  XLS_RECORD_BLANK  // compat
 
-#if defined(_AIX) || defined(__sun)
+#ifdef AIX
 #pragma pack(1)
 #else
 #pragma pack(push, 1)
@@ -101,7 +101,6 @@ typedef struct BIFF
     WORD year;
     DWORD flags;
     DWORD min_ver;
-    BYTE buf[100];
 }
 BIFF;
 
@@ -124,7 +123,7 @@ typedef struct BOUNDSHEET
     DWORD	filepos;
     BYTE	type;
     BYTE	visible;
-    BYTE	name[1];
+    char	name[];
 }
 BOUNDSHEET;
 
@@ -195,7 +194,7 @@ typedef struct MULRK
 	struct {
 		WORD	xf;
 		DWORD_UA value;
-	}		rk[1];
+	}		rk[];
 	//WORD	last_col;
 }
 MULRK;
@@ -204,7 +203,7 @@ typedef struct MULBLANK
 {
     WORD	row;
     WORD	col;
-    WORD	xf[1];
+    WORD	xf[];
 	//WORD	last_col;
 }
 MULBLANK;
@@ -222,7 +221,7 @@ typedef struct LABEL
     WORD	row;
     WORD	col;
     WORD	xf;
-    BYTE	value[1]; // var
+    BYTE	value[]; // var
 }
 LABEL;
 typedef LABEL LABELSST;
@@ -313,14 +312,14 @@ typedef struct FONT
     BYTE	family;
     BYTE	charset;
     BYTE	notused;
-    BYTE	name;
+    char    name[];
 }
 FONT;
 
 typedef struct FORMAT
 {
     WORD	index;
-    BYTE	value[1];
+    char	value[];
 }
 FORMAT;
 
@@ -336,7 +335,7 @@ typedef	struct st_sheet
         DWORD filepos;
         BYTE visibility;
         BYTE type;
-        BYTE* name;
+        char * name;
     }
     * sheet;
 }
@@ -355,7 +354,7 @@ typedef	struct st_font
         BYTE	underline;
         BYTE	family;
         BYTE	charset;
-        BYTE*	name;
+        char *	name;
     }
     * font;
 }
@@ -367,7 +366,7 @@ typedef struct st_format
     struct st_format_data
     {
          WORD index;
-         BYTE *value;
+         char *value;
     }
     * format;
 }
@@ -405,7 +404,7 @@ typedef	struct st_sst
     DWORD lastsz;
     struct str_sst_string
     {
-        BYTE* str;
+        char * str;
     }
     * string;
 }
@@ -421,7 +420,7 @@ typedef	struct st_cell
         WORD	row;
         WORD	col;
         WORD	xf;
-        BYTE*	str;		// String value;
+        char *	str;		// String value;
         double	d;
         int32_t	l;
         WORD	width;		// Width of col
--- r-cran-readxl-1.0.0.orig/src/libxls/xlstool.h
+++ r-cran-readxl-1.0.0/src/libxls/xlstool.h
@@ -31,15 +31,13 @@
  */
 
 #include "libxls/xlsstruct.h"
-/* Mask illegal functions for CMD check */
-#include "cran.h"
 
 extern void dumpbuf(BYTE* fname,long size,BYTE* buf);
 extern void verbose(char* str);
 
-extern BYTE *utf8_decode(BYTE *str, DWORD len, char *encoding);
-extern BYTE* unicode_decode(const BYTE *s, int len, size_t *newlen, const char* encoding);
-extern BYTE* get_string(BYTE *s,BYTE is2, BYTE isUnicode, char *charset);
+extern char *utf8_decode(const char *str, DWORD len, char *encoding);
+extern char *unicode_decode(const char *s, size_t len, size_t *newlen, const char* encoding);
+extern char *get_string(const char *s, size_t len, BYTE is2, BYTE isUnicode, char *charset);
 extern DWORD xls_getColor(const WORD color,WORD def);
 
 extern void xls_showBookInfo(xlsWorkBook* pWB);
@@ -49,6 +47,6 @@ extern void xls_showCell(struct st_cell_
 extern void xls_showFont(struct st_font_data* font);
 extern void xls_showXF(XF8* xf);
 extern void xls_showFormat(struct st_format_data* format);
-extern BYTE* xls_getfcell(xlsWorkBook* pWB,struct st_cell_data* cell,BYTE *label);
+extern char* xls_getfcell(xlsWorkBook* pWB, struct st_cell_data* cell, WORD *label);
 extern char* xls_getCSS(xlsWorkBook* pWB);
 extern void xls_showBOF(BOF* bof);
--- r-cran-readxl-1.0.0.orig/src/libxls/xlstypes.h
+++ r-cran-readxl-1.0.0/src/libxls/xlstypes.h
@@ -47,10 +47,24 @@ typedef uint16_t			WORD_UA		__attribute_
 typedef uint32_t			DWORD_UA	__attribute__ ((aligned (1)));	// 4 bytes
 #endif
 
-#ifdef _WIN32
+// Windows
+#if defined(_MSC_VER) && defined(WIN32)
+
 typedef unsigned __int64	unsigned64_t;
+
+// not windows
 #else
+
+#if defined(_UINT64_T)
+
 typedef uint64_t			unsigned64_t;
+
+#else
+
+typedef unsigned long long	unsigned64_t;
+
+// _UINT64_T
+#endif
 #endif
 
 #endif
--- r-cran-readxl-1.0.0.orig/src/ole.c
+++ r-cran-readxl-1.0.0/src/ole.c
@@ -38,13 +38,9 @@
 #include <stdio.h>
 #include <stdlib.h>
 
-#include <assert.h>
-
 #include "libxls/ole.h"
 #include "libxls/xlstool.h"
 #include "libxls/endian.h"
-/* Mask illegal functions for CMD check */
-#include "cran.h"
 
 extern int xls_debug;
 
@@ -56,55 +52,72 @@ static const DWORD ENDOFCHAIN	= 0xFFFFFF
 static const DWORD FREESECT		= 0xFFFFFFFF;	// -1
 
 static size_t sector_pos(OLE2* ole2, size_t sid);
-static int sector_read(OLE2* ole2, BYTE *buffer, size_t sid);
-static size_t read_MSAT(OLE2* ole2, OLE2Header *oleh);
+static ssize_t sector_read(OLE2* ole2, void *buffer, size_t sid);
+static ssize_t read_MSAT(OLE2* ole2, OLE2Header *oleh);
+static void *ole_malloc(size_t len);
+
+static void *ole_malloc(size_t len) {
+    if (len > (1<<24) || len == 0) {
+        return NULL;
+    }
+    return malloc(len);
+}
 
 // Read next sector of stream
-void ole2_bufread(OLE2Stream* olest) 
+int ole2_bufread(OLE2Stream* olest) 
 {
 	BYTE *ptr;
 
-	assert(olest);
-	assert(olest->ole);
+    if (olest == NULL || olest->ole == NULL)
+        return -1;
 
     if ((DWORD)olest->fatpos!=ENDOFCHAIN)
     {
 		if(olest->sfat) {
-			assert(olest->ole->SSAT);
-			assert(olest->buf);
-			assert(olest->ole->SSecID);
+            if (olest->ole->SSAT == NULL || olest->buf == NULL || olest->ole->SSecID == NULL)
+                return -1;
+
+            if (olest->fatpos*olest->ole->lssector + olest->bufsize > olest->ole->SSATCount) {
+                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSAT\n", (int)olest->fatpos);
+                return -1;
+            }
 
 			ptr = olest->ole->SSAT + olest->fatpos*olest->ole->lssector;
 			memcpy(olest->buf, ptr, olest->bufsize); 
 
+            if (olest->fatpos >= olest->ole->SSecIDCount) {
+                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SSecID[%d]\n",
+                        (int)olest->fatpos, olest->ole->SSecIDCount);
+                return -1;
+            }
+
 			olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
 			olest->pos=0;
 			olest->cfat++;
 		} else {
+			if ((int)olest->fatpos < 0 ||
+                sector_read(olest->ole, olest->buf, olest->fatpos) == -1) {
+                if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", (int)olest->fatpos);
+                return -1;
+            }
 
-			assert(olest->fatpos >= 0);
-
-			//printf("fatpos: %d max=%u\n",olest->fatpos, (olest->ole->cfat*olest->ole->lsector)/4);
-			if(olest->fatpos > (olest->ole->cfat*olest->ole->lsector)/4) exit(-1);
+            if (olest->fatpos >= olest->ole->SecIDCount) {
+                if (xls_debug) fprintf(stderr, "Error: fatpos %d out-of-bounds for SecID[%d]\n",
+                        (int)olest->fatpos, olest->ole->SecIDCount);
+                return -1;
+            }
 
-#if 0 // TODO: remove
-			fseek(olest->ole->file,olest->fatpos*olest->ole->lsector+512,0);
-			ret = fread(olest->buf,1,olest->bufsize,olest->ole->file);
-			assert(ret == olest->bufsize);
-#endif
-			assert((int)olest->fatpos >= 0);
-			sector_read(olest->ole, olest->buf, olest->fatpos);
-			//printf("Fat val: %d[0x%X]\n",olest->fatpos,olest->ole->SecID[olest->fatpos], olest->ole->SecID[olest->fatpos]);
 			olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]);
 			olest->pos=0;
 			olest->cfat++;
 		}
     }
 	// else printf("ENDOFCHAIN!!!\n");
+    return 0;
 }
 
 // Read part of stream
-size_t ole2_read(void* buf,size_t size,size_t count,OLE2Stream* olest)
+ssize_t ole2_read(void* buf, size_t size, size_t count, OLE2Stream* olest)
 {
     size_t didReadCount=0;
     size_t totalReadCount;
@@ -126,7 +139,7 @@ size_t ole2_read(void* buf,size_t size,s
 		//printf("  totalReadCount=%d (rem=%d size*count=%ld)\n", totalReadCount, rem, size*count);
 	}
 
-	while ((!olest->eof) && (didReadCount!=totalReadCount))
+	while ((!olest->eof) && (didReadCount < totalReadCount))
 	{
 		unsigned long remainingBytes;
 
@@ -145,9 +158,9 @@ size_t ole2_read(void* buf,size_t size,s
 			memcpy((BYTE*)buf + didReadCount, olest->buf + olest->pos, remainingBytes);
 			olest->pos		+= remainingBytes;
 			didReadCount	+= remainingBytes;
-			ole2_bufread(olest);
+			if (ole2_bufread(olest) == -1)
+                return -1;
 		}
-		assert(didReadCount <= totalReadCount);
 		//printf("  if(fatpos=0x%X==EOC=0x%X) && (pos=%d >= bufsize=%d)\n", olest->fatpos, ENDOFCHAIN, olest->pos, olest->bufsize);
 		if (((DWORD)olest->fatpos == ENDOFCHAIN) && (olest->pos >= olest->bufsize))
 		{
@@ -156,22 +169,25 @@ size_t ole2_read(void* buf,size_t size,s
 
 		//printf("  eof=%d (didReadCount=%ld != totalReadCount=%ld)\n", olest->eof, didReadCount, totalReadCount);
 	}
+    if (didReadCount > totalReadCount)
+        return -1;
+
 	// printf("  didReadCount=%ld EOF=%d\n", didReadCount, olest->eof);
 	// printf("=====\n");
 
 #ifdef OLE_DEBUG
-    printf("----------------------------------------------\n");
-    printf("ole2_read (end)\n");
-    printf("start:		%li \n",olest->start);
-    printf("pos:		%li \n",olest->pos);
-    printf("cfat:		%d \n",olest->cfat);
-    printf("size:		%d \n",olest->size);
-    printf("fatpos:		%li \n",olest->fatpos);
-    printf("bufsize:		%li \n",olest->bufsize);
-    printf("eof:		%d \n",olest->eof);
+    fprintf(stderr, "----------------------------------------------\n");
+    fprintf(stderr, "ole2_read (end)\n");
+    fprintf(stderr, "start:		%d \n",olest->start);
+    fprintf(stderr, "pos:		%d \n",(int)olest->pos);
+    fprintf(stderr, "cfat:		%d \n",(int)olest->cfat);
+    fprintf(stderr, "size:		%d \n",(int)olest->size);
+    fprintf(stderr, "fatpos:		%d \n",(int)olest->fatpos);
+    fprintf(stderr, "bufsize:		%d \n",(int)olest->bufsize);
+    fprintf(stderr, "eof:		%d \n",olest->eof);
 #endif
 
-    return(didReadCount);
+    return didReadCount;
 }
 
 // Open stream in logical ole file
@@ -180,8 +196,8 @@ OLE2Stream* ole2_sopen(OLE2* ole,DWORD s
     OLE2Stream* olest=NULL;
 
 #ifdef OLE_DEBUG
-    printf("----------------------------------------------\n");
-    printf("ole2_sopen start=%lXh\n", start);
+    fprintf(stderr, "----------------------------------------------\n");
+    fprintf(stderr, "ole2_sopen start=%Xh\n", start);
 #endif
 
 	olest=(OLE2Stream*)calloc(1, sizeof(OLE2Stream));
@@ -198,7 +214,7 @@ OLE2Stream* ole2_sopen(OLE2* ole,DWORD s
 	} else {
 		olest->bufsize=ole->lsector;
 	}
-	olest->buf=malloc(olest->bufsize);
+	olest->buf = ole_malloc(olest->bufsize);
 	ole2_bufread(olest);
 
 	// if(xls_debug) printf("sopen: sector=%d next=%d\n", start, olest->fatpos);
@@ -206,8 +222,11 @@ OLE2Stream* ole2_sopen(OLE2* ole,DWORD s
 }
 
 // Move in stream
-void ole2_seek(OLE2Stream* olest,DWORD ofs)
+int ole2_seek(OLE2Stream* olest,DWORD ofs)
 {
+#ifdef OLE_DEBUG
+    fprintf(stderr, "SEEK %x\n", ofs);
+#endif
 	if(olest->sfat) {
 		ldiv_t div_rez=ldiv(ofs,olest->ole->lssector);
 		int i;
@@ -215,8 +234,11 @@ void ole2_seek(OLE2Stream* olest,DWORD o
 
 		if (div_rez.quot!=0)
 		{
-			for (i=0;i<div_rez.quot;i++)
+			for (i=0;i<div_rez.quot;i++) {
+                if (olest->fatpos >= olest->ole->SSecIDCount)
+                    return -1;
 				olest->fatpos=xlsIntVal(olest->ole->SSecID[olest->fatpos]);
+            }
 		}
 
 		ole2_bufread(olest);
@@ -227,12 +249,18 @@ void ole2_seek(OLE2Stream* olest,DWORD o
 	} else {
 		ldiv_t div_rez=ldiv(ofs,olest->ole->lsector);
 		int i;
+#ifdef OLE_DEBUG
+        fprintf(stderr, "seeking fatpos%lu start %u\n", olest->fatpos, olest->start);
+#endif
 		olest->fatpos=olest->start;
 
 		if (div_rez.quot!=0)
 		{
-			for (i=0;i<div_rez.quot;i++)
-				olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]);
+			for (i=0;i<div_rez.quot;i++) {
+                if (olest->fatpos >= olest->ole->SecIDCount)
+                    return -1;
+                olest->fatpos=xlsIntVal(olest->ole->SecID[olest->fatpos]);
+            }
 		}
 
 		ole2_bufread(olest);
@@ -241,76 +269,88 @@ void ole2_seek(OLE2Stream* olest,DWORD o
 		olest->cfat=div_rez.quot;
 		//printf("%i=%i %i\n",ofs,div_rez.quot,div_rez.rem);
 	}
+    return 0;
 }
 
 // Open logical file contained in physical OLE file
-OLE2Stream*  ole2_fopen(OLE2* ole,BYTE* file)
+OLE2Stream*  ole2_fopen(OLE2* ole, const char *file)
 {
-    OLE2Stream* olest;
     int i;
 
 #ifdef OLE_DEBUG
-    printf("----------------------------------------------\n");
-    printf("ole2_fopen %s\n", file);
+    fprintf(stderr, "----------------------------------------------\n");
+    fprintf(stderr, "ole2_fopen %s\n", file);
 #endif
 
     for (i=0;i<ole->files.count;i++) {
-		BYTE *str = ole->files.file[i].name;
+		char *str = ole->files.file[i].name;
 #ifdef OLE_DEBUG
-		printf("----------------------------------------------\n");
-		printf("ole2_fopen found %s\n", str);
+		fprintf(stderr, "----------------------------------------------\n");
+		fprintf(stderr, "ole2_fopen found %s\n", str);
 #endif
-        if (str && strcmp((char *)str,(char *)file)==0)	// newer versions of Excel don't write the "Root Entry" string for the first set of data
+        if (str && strcmp(str,file)==0)	// newer versions of Excel don't write the "Root Entry" string for the first set of data
         {
-            olest=ole2_sopen(ole,ole->files.file[i].start,ole->files.file[i].size);
-            return(olest);
+            return ole2_sopen(ole,ole->files.file[i].start,ole->files.file[i].size);
         }
 	}
-    return(NULL);
+    return NULL;
 }
 
-// Open physical file
-OLE2* ole2_open(const BYTE *file)
-{
-    //BYTE buf[1024];
-    OLE2Header* oleh;
-    OLE2* ole;
-    OLE2Stream* olest;
-    PSS*	pss;
-    BYTE* name = NULL;
+int ole2_fseek(OLE2 *ole2, size_t pos) {
+    if (ole2->file)
+        return fseek(ole2->file, pos, SEEK_SET);
 
-#ifdef OLE_DEBUG
-    printf("----------------------------------------------\n");
-    printf("ole2_open %s\n", file);
-#endif
+    if (pos > ole2->buffer_len)
+        return -1;
 
-	if(xls_debug) printf("ole2_open: %s\n", file);
-    ole=(OLE2*)calloc(1, sizeof(OLE2));
-    if (!(ole->file=fopen((char *)file,"rb")))
-    {
-        if(xls_debug) printf("File not found\n");
-        free(ole);
-        return(NULL);
+    ole2->buffer_pos = pos;
+    return 0;
+}
+
+size_t ole2_fread(OLE2 *ole2, void *buffer, size_t size, size_t nitems) {
+    if (ole2->file)
+        return fread(buffer, size, nitems, ole2->file);
+
+    size_t i = 0;
+    for (i=0; i<nitems; i++) {
+        if (ole2->buffer_pos + size > ole2->buffer_len)
+            break;
+
+        memcpy(buffer, (const char *)ole2->buffer + ole2->buffer_pos, size);
+        ole2->buffer_pos += size;
+    }
+    return i;
+}
+
+
+// read header and check magic numbers
+static ssize_t ole2_read_header(OLE2 *ole) {
+    ssize_t bytes_read = 0, total_bytes_read = 0;
+    OLE2Header *oleh = malloc(512);
+    if (ole2_fread(ole, oleh, 512, 1) != 1) {
+        total_bytes_read = -1;
+        goto cleanup;
     }
-    // read header and check magic numbers
-    oleh=(OLE2Header*)malloc(512);
-    fread(oleh,1,512,ole->file);
+    total_bytes_read += 512;
     xlsConvertHeader(oleh);
 
 	// make sure the file looks good. Note: this code only works on Little Endian machines
 	if(oleh->id[0] != 0xE011CFD0 || oleh->id[1] != 0xE11AB1A1 || oleh->byteorder != 0xFFFE) {
-		fclose(ole->file);
-        printf("Not an excel file\n");
-		free(ole);
-		return NULL;
+        if (xls_debug) fprintf(stderr, "Not an excel file\n");
+        total_bytes_read = -1;
+        goto cleanup;
 	}
 
     //ole->lsector=(WORD)pow(2,oleh->lsector);
     //ole->lssector=(WORD)pow(2,oleh->lssector);
 	ole->lsector=512;
     ole->lssector=64;
-	assert(oleh->lsectorB==9);	// 2**9 == 512
-	assert(oleh->lssectorB==6);	// 2**6 == 64
+
+	if (oleh->lsectorB != 9 || oleh->lssectorB != 6) {	// 2**9 == 512, 2**6 == 64
+        if (xls_debug) fprintf(stderr, "Unexpected sector size\n");
+        total_bytes_read = -1;
+        goto cleanup;
+    }
 	
     ole->cfat=oleh->cfat;
     ole->dirstart=oleh->dirstart;
@@ -322,52 +362,66 @@ OLE2* ole2_open(const BYTE *file)
     ole->files.count=0;
 
 #ifdef OLE_DEBUG
-		printf("==== OLE HEADER ====\n");
+		fprintf(stderr, "==== OLE HEADER ====\n");
 		//printf ("Header Size:   %i \n", sizeof(OLE2Header));
 		//printf ("id[0]-id[1]:   %X-%X \n", oleh->id[0], oleh->id[1]);
-		printf ("verminor:      %X \n",oleh->verminor);
-		printf ("verdll:        %X \n",oleh->verdll);
+		fprintf(stderr, "verminor:      %X \n",oleh->verminor);
+		fprintf(stderr, "verdll:        %X \n",oleh->verdll);
 		//printf ("Byte order:    %X \n",oleh->byteorder);
-		printf ("sect len:      %X (%i)\n",ole->lsector,ole->lsector);		// ole
-		printf ("mini len:      %X (%i)\n",ole->lssector,ole->lssector);	// ole
-		printf ("Fat sect.:     %i \n",oleh->cfat);
-		printf ("Dir Start:     %i \n",oleh->dirstart);
+		fprintf(stderr, "sect len:      %X (%i)\n",ole->lsector,ole->lsector);		// ole
+		fprintf(stderr, "mini len:      %X (%i)\n",ole->lssector,ole->lssector);	// ole
+		fprintf(stderr, "Fat sect.:     %i \n",oleh->cfat);
+		fprintf(stderr, "Dir Start:     %i \n",oleh->dirstart);
 		
-		printf ("Mini Cutoff:   %i \n",oleh->sectorcutoff);
-		printf ("MiniFat Start: %X \n",oleh->sfatstart);
-		printf ("Count MFat:    %i \n",oleh->csfat);
-		printf ("Dif start:     %X \n",oleh->difstart);
-		printf ("Count Dif:     %i \n",oleh->cdif);
-		printf ("Fat Size:      %u (0x%X) \n",oleh->cfat*ole->lsector,oleh->cfat*ole->lsector);
+		fprintf(stderr, "Mini Cutoff:   %i \n",oleh->sectorcutoff);
+		fprintf(stderr, "MiniFat Start: %X \n",oleh->sfatstart);
+		fprintf(stderr, "Count MFat:    %i \n",oleh->csfat);
+		fprintf(stderr, "Dif start:     %X \n",oleh->difstart);
+		fprintf(stderr, "Count Dif:     %i \n",oleh->cdif);
+		fprintf(stderr, "Fat Size:      %u (0x%X) \n",oleh->cfat*ole->lsector,oleh->cfat*ole->lsector);
 #endif
     // read directory entries
-    read_MSAT(ole, oleh);
+    if ((bytes_read = read_MSAT(ole, oleh)) == -1) {
+        total_bytes_read = -1;
+        goto cleanup;
+    }
+    total_bytes_read += bytes_read;
 
+cleanup:
+    free(oleh);
+
+    return total_bytes_read;
+}
+
+static ssize_t ole2_read_body(OLE2 *ole) {
 	// reuse this buffer
-    pss = (PSS*)oleh;
-	// oleh = (void *)NULL; // Not needed as oleh not used from here on
-	
-    olest=ole2_sopen(ole,ole->dirstart, -1);
-    do
-    {
-        ole2_read(pss,1,sizeof(PSS),olest);
+    PSS *pss = malloc(512);
+    OLE2Stream *olest=ole2_sopen(ole,ole->dirstart, -1);
+    char* name = NULL;
+    ssize_t bytes_read = 0, total_bytes_read = 0;
+
+    do {
+        if ((bytes_read = ole2_read(pss,1,sizeof(PSS),olest)) == -1) {
+            total_bytes_read = -1;
+            goto cleanup;
+        }
+        total_bytes_read += bytes_read;
         xlsConvertPss(pss);
+        if (pss->bsize > sizeof(pss->name)) {
+            total_bytes_read = -1;
+            goto cleanup;
+        }
         name=unicode_decode(pss->name, pss->bsize, 0, "UTF-8");
 #ifdef OLE_DEBUG	
-		printf("OLE NAME: %s count=%d\n", name, ole->files.count);
+		fprintf(stderr, "OLE NAME: %s count=%d\n", name, (int)ole->files.count);
 #endif
         if (pss->type == PS_USER_ROOT || pss->type == PS_USER_STREAM) // (name!=NULL) // 
         {
 
 #ifdef OLE_DEBUG		
-			printf("OLE TYPE: %s file=%d \n", pss->type == PS_USER_ROOT ? "root" : "user", ole->files.count);
+			fprintf(stderr, "OLE TYPE: %s file=%d \n", pss->type == PS_USER_ROOT ? "root" : "user", (int)ole->files.count);
 #endif		
-            if (ole->files.count==0)
-            {
-                ole->files.file=malloc(sizeof(struct st_olefiles_data));
-            } else {
-                ole->files.file=realloc(ole->files.file,(ole->files.count+1)*sizeof(struct st_olefiles_data));
-            }
+            ole->files.file = realloc(ole->files.file,(ole->files.count+1)*sizeof(struct st_olefiles_data));
             ole->files.file[ole->files.count].name=name;
             ole->files.file[ole->files.count].start=pss->sstart;
             ole->files.file[ole->files.count].size=pss->size;
@@ -375,41 +429,45 @@ OLE2* ole2_open(const BYTE *file)
 			
 			if(pss->sstart == ENDOFCHAIN) {
 				if (xls_debug) verbose("END OF CHAIN\n");
-			} else
-			if(pss->type == PS_USER_STREAM) {
+			} else if(pss->type == PS_USER_STREAM) {
 #ifdef OLE_DEBUG
-					printf("----------------------------------------------\n");
-					printf("name: %s (size=%d [c=%c])\n", name, pss->bsize, name ? name[0]:' ');
-					printf("bsize %i\n",pss->bsize);
-					printf("type %i\n",pss->type);
-					printf("flag %i\n",pss->flag);
-					printf("left %X\n",pss->left);
-					printf("right %X\n",pss->right);
-					printf("child %X\n",pss->child);
-					printf("guid %.4X-%.4X-%.4X-%.4X %.4X-%.4X-%.4X-%.4X\n",pss->guid[0],pss->guid[1],pss->guid[2],pss->guid[3],
+					fprintf(stderr, "----------------------------------------------\n");
+					fprintf(stderr, "name: %s (size=%d [c=%c])\n", name, pss->bsize, name ? name[0]:' ');
+					fprintf(stderr, "bsize %i\n",pss->bsize);
+					fprintf(stderr, "type %i\n",pss->type);
+					fprintf(stderr, "flag %i\n",pss->flag);
+					fprintf(stderr, "left %X\n",pss->left);
+					fprintf(stderr, "right %X\n",pss->right);
+					fprintf(stderr, "child %X\n",pss->child);
+					fprintf(stderr, "guid %.4X-%.4X-%.4X-%.4X %.4X-%.4X-%.4X-%.4X\n",
+                            pss->guid[0],pss->guid[1],pss->guid[2],pss->guid[3],
 						pss->guid[4],pss->guid[5],pss->guid[6],pss->guid[7]);
-					printf("user flag %.4X\n",pss->userflags);
-					printf("sstart %.4d\n",pss->sstart);
-					printf("size %.4d\n",pss->size);
+					fprintf(stderr, "user flag %.4X\n",pss->userflags);
+					fprintf(stderr, "sstart %.4d\n",pss->sstart);
+					fprintf(stderr, "size %.4d\n",pss->size);
 #endif
-			} else
-			if(pss->type == PS_USER_ROOT) {
+			} else if(pss->type == PS_USER_ROOT) {
 				DWORD sector, k, blocks;
 				BYTE *wptr;
 				
 				blocks = (pss->size + (ole->lsector - 1)) / ole->lsector;	// count partial
-				ole->SSAT = (BYTE *)malloc(blocks*ole->lsector);
+				if ((ole->SSAT = ole_malloc(blocks*ole->lsector)) == NULL) {
+                    total_bytes_read = -1;
+                    goto cleanup;
+                }
+                ole->SSATCount = blocks*ole->lsector;
 				// printf("blocks %d\n", blocks);
 
-				assert(ole->SSecID);
-				
 				sector = pss->sstart;
-				wptr=(BYTE*)ole->SSAT;
+				wptr = (BYTE*)ole->SSAT;
 				for(k=0; k<blocks; ++k) {
 					// printf("block %d sector %d\n", k, sector);
-					assert(sector != ENDOFCHAIN);
-					fseek(ole->file,sector*ole->lsector+512,0);
-					fread(wptr,1,ole->lsector,ole->file);
+                    if (sector == ENDOFCHAIN || sector_read(ole, wptr, sector) == -1) {
+                        if (xls_debug) fprintf(stderr, "Unable to read sector #%d\n", sector);
+                        total_bytes_read = -1;
+                        goto cleanup;
+                    }
+                    total_bytes_read += ole->lsector;
 					wptr += ole->lsector;
 					sector = xlsIntVal(ole->SecID[sector]);
 				}
@@ -420,16 +478,72 @@ OLE2* ole2_open(const BYTE *file)
     }
     while (!olest->eof);
 
+cleanup:
 	ole2_fclose(olest);
     free(pss);
 
+    return total_bytes_read;
+}
+
+// Open in-memory buffer
+OLE2 *ole2_open_buffer(const void *buffer, size_t len) {
+    OLE2 *ole=(OLE2*)calloc(1, sizeof(OLE2));
+
+    ole->buffer = buffer;
+    ole->buffer_len = len;
+
+    if (ole2_read_header(ole) == -1) {
+        free(ole);
+        return NULL;
+    }
+
+    if (ole2_read_body(ole) == -1) {
+        free(ole);
+        return NULL;
+    }
+
+    return ole;
+}
+
+// Open physical file
+OLE2* ole2_open_file(const char *file)
+{
+    OLE2* ole = NULL;
+
+#ifdef OLE_DEBUG
+    fprintf(stderr, "----------------------------------------------\n");
+    fprintf(stderr, "ole2_open_file %s\n", file);
+#endif
+
+	if(xls_debug) printf("ole2_open: %s\n", file);
+    ole=(OLE2*)calloc(1, sizeof(OLE2));
+
+    if (!(ole->file=fopen(file, "rb"))) {
+        if(xls_debug) fprintf(stderr, "File not found\n");
+        free(ole);
+        return NULL;
+    }
+
+    if (ole2_read_header(ole) == -1) {
+		fclose(ole->file);
+        free(ole);
+        return NULL;
+    }
+
+    if (ole2_read_body(ole) == -1) {
+		fclose(ole->file);
+        free(ole);
+        return NULL;
+    }
+
     return ole;
 }
 
 void ole2_close(OLE2* ole2)
 {
     int i;
-	fclose(ole2->file);
+    if (ole2->file)
+        fclose(ole2->file);
 
 	for(i=0; i<ole2->files.count; ++i) {
 		free(ole2->files.file[i].name);
@@ -453,112 +567,187 @@ static size_t sector_pos(OLE2* ole2, siz
     return 512 + sid * ole2->lsector;
 }
 // Read one sector from its sid
-static int sector_read(OLE2* ole2, BYTE *buffer, size_t sid)
+static ssize_t sector_read(OLE2* ole2, void *buffer, size_t sid)
 {
 	size_t num;
 	size_t seeked;
 
 	//printf("sector_read: sid=%zu (0x%zx) lsector=%u sector_pos=%zu\n", sid, sid, ole2->lsector, sector_pos(ole2, sid) );
-    seeked = fseek(ole2->file, sector_pos(ole2, sid), SEEK_SET);
+    seeked = ole2_fseek(ole2, sector_pos(ole2, sid));
 	if(seeked != 0) {
-		printf("seek: wanted to seek to sector %zu (0x%zx) loc=%zu\n", sid, sid, sector_pos(ole2, sid));
-	}
-	assert(seeked == 0);
-	
-	num = fread(buffer, ole2->lsector, 1, ole2->file);
-	if(num != 1) {
-		fprintf(stderr, "fread: wanted 1 got %zu loc=%zu\n", num, sector_pos(ole2, sid));
-	}
-	assert(num == 1);
+		if (xls_debug) fprintf(stderr, "Error: wanted to seek to sector %zu (0x%zx) loc=%zu\n", sid, sid, sector_pos(ole2, sid));
+        return -1;
+    }
 
-    return 0;
+	num = ole2_fread(ole2, buffer, ole2->lsector, 1);
+    if(num != 1) {
+        if (xls_debug) fprintf(stderr, "Error: fread wanted 1 got %zu loc=%zu\n", num, sector_pos(ole2, sid));
+        return -1;
+    }
+
+    return ole2->lsector;
 }
 
-// Read MSAT
-static size_t read_MSAT(OLE2* ole2, OLE2Header* oleh)
-{
+// read first 109 sectors of MSAT from header
+static ssize_t read_MSAT_header(OLE2* ole2, OLE2Header* oleh, int sectorCount) {
+    BYTE *sector = (BYTE*)ole2->SecID;
+    ssize_t bytes_read = 0, total_bytes_read = 0;
     int sectorNum;
 
-    // reconstitution of the MSAT
-    ole2->SecID=malloc(ole2->cfat*ole2->lsector);
-
-    // read first 109 sectors of MSAT from header
+    for (sectorNum = 0; sectorNum < sectorCount; sectorNum++)
     {
-        int count;
-        count = (ole2->cfat < 109) ? ole2->cfat : 109;
-        for (sectorNum = 0; sectorNum < count; sectorNum++)
-        {
-			assert(sectorNum >= 0);
-            sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, oleh->MSAT[sectorNum]);
+        if ((bytes_read = sector_read(ole2, sector, oleh->MSAT[sectorNum])) == -1) {
+            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", oleh->MSAT[sectorNum]);
+            return -1;
         }
+        sector += ole2->lsector;
+        total_bytes_read += bytes_read;
     }
+    return total_bytes_read;
+}
 
-    // Add additionnal sectors of the MSAT
+// Add additional sectors of the MSAT
+static ssize_t read_MSAT_body(OLE2 *ole2, int sectorOffset, int sectorCount) {
+    DWORD sid = ole2->difstart;
+    ssize_t bytes_read = 0, total_bytes_read = 0;
+    int sectorNum = sectorOffset;
+
+    BYTE *sector = ole_malloc(ole2->lsector);
+    //printf("sid=%u (0x%x) sector=%u\n", sid, sid, ole2->lsector);
+    while (sid != ENDOFCHAIN && sid != FREESECT) // FREESECT only here due to an actual file that requires it (old Apple Numbers bug)
     {
-        DWORD sid = ole2->difstart;
+        int posInSector;
+        // read MSAT sector
+        if ((bytes_read = sector_read(ole2, sector, sid)) == -1) {
+            total_bytes_read = -1;
+            if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", sid);
+            goto cleanup;
+        }
+        total_bytes_read += bytes_read;
 
-		BYTE *sector = malloc(ole2->lsector);
-		//printf("sid=%u (0x%x) sector=%u\n", sid, sid, ole2->lsector);
-        while (sid != ENDOFCHAIN && sid != FREESECT) // FREESECT only here due to an actual file that requires it (old Apple Numbers bug)
-		{
-           int posInSector;
-           // read MSAT sector
-           sector_read(ole2, sector, sid);
-
-           // read content
-           for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++)
-		   {
-              DWORD s = *(DWORD_UA *)(sector + posInSector*4);
-              //printf("   s[%d]=%d (0x%x)\n", posInSector, s, s);
-
-              if (s != FREESECT)
-                {
-                 sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, s);
-                 sectorNum++;
+        // read content
+        for (posInSector = 0; posInSector < (ole2->lsector-4)/4; posInSector++)
+        {
+            DWORD s = *(DWORD_UA *)(sector + posInSector*4);
+            //printf("   s[%d]=%d (0x%x)\n", posInSector, s, s);
+
+            if (s != ENDOFCHAIN && s != FREESECT) // see patch in Bug 31. For very large files
+            {
+                if (sectorNum == sectorCount) {
+                    if (xls_debug) fprintf(stderr, "Error: Unable to seek to sector #%d\n", s);
+                    total_bytes_read = -1;
+                    goto cleanup;
                 }
-			}
-			sid = *(DWORD_UA *)(sector + posInSector*4);
-			//printf("   s[%d]=%d (0x%x)\n", posInSector, sid, sid);
-		}
-		free(sector);
+                if ((bytes_read = sector_read(ole2, (BYTE*)(ole2->SecID)+sectorNum*ole2->lsector, s)) == -1) {
+                    if (xls_debug) fprintf(stderr, "Error: Unable to read sector #%d\n", s);
+                    total_bytes_read = -1;
+                    goto cleanup;
+                }
+                total_bytes_read += bytes_read;
+                sectorNum++;
+            }
+        }
+        sid = *(DWORD_UA *)(sector + posInSector*4);
+        //printf("   s[%d]=%d (0x%x)\n", posInSector, sid, sid);
     }
 #ifdef OLE_DEBUG
-	if(xls_debug) {
-		//printf("==== READ IN SECTORS FOR MSAT TABLE====\n");
-		int i;
-		for(i=0; i<512/4; ++i) {	// just the first block
-			if(ole2->SecID[i] != FREESECT) printf("SecID[%d]=%d\n", i, ole2->SecID[i]);
-		}
-	}
-	//exit(0);
+    if(xls_debug) {
+        //printf("==== READ IN SECTORS FOR MSAT TABLE====\n");
+        int i;
+        for(i=0; i<512/4; ++i) {	// just the first block
+            if(ole2->SecID[i] != FREESECT) printf("SecID[%d]=%d\n", i, ole2->SecID[i]);
+        }
+    }
+    //exit(0);
 #endif
 
-	// read in short table
+cleanup:
+    free(sector);
+    return total_bytes_read;
+}
+
+// read in short table
+static ssize_t read_MSAT_trailer(OLE2 *ole2) {
+    ssize_t total_bytes_read = 0;
+    DWORD sector, k;
+    BYTE *wptr;
+
 	if(ole2->sfatstart != ENDOFCHAIN) {
-		DWORD sector, k;
-		BYTE *wptr;
-		
-		ole2->SSecID = (DWORD *)malloc(ole2->csfat*ole2->lsector);
+		if ((ole2->SSecID = ole_malloc(ole2->csfat*(size_t)ole2->lsector)) == NULL) {
+            return -1;
+        }
+        ole2->SSecIDCount = ole2->csfat*(size_t)ole2->lsector/4;
 		sector = ole2->sfatstart;
 		wptr=(BYTE*)ole2->SSecID;
 		for(k=0; k<ole2->csfat; ++k) {
-			assert(sector != ENDOFCHAIN);
-			fseek(ole2->file,sector*ole2->lsector+512,0);
-			fread(wptr,1,ole2->lsector,ole2->file);
+			if (sector == ENDOFCHAIN || sector_read(ole2, wptr, sector) == -1) {
+                total_bytes_read = -1;
+                goto cleanup;
+            }
 			wptr += ole2->lsector;
+            total_bytes_read += ole2->lsector;
 			sector = ole2->SecID[sector];
 		}
 #ifdef OLE_DEBUG
 		if(xls_debug) {
 			int i;
-			for(i=0; i<512/4; ++i) {
-				if(ole2->SSecID[i] != FREESECT) printf("SSecID[%d]=%d\n", i, ole2->SSecID[i]);
+			for(i=0; i<ole2->csfat; ++i) {
+				if(ole2->SSecID[i] != FREESECT) fprintf(stderr, "SSecID[%d]=%d\n", i, ole2->SSecID[i]);
 			}
 		}
 #endif
 	}
 
-    return 0;
+cleanup:
+    return total_bytes_read;
 }
 
 
+// Read MSAT
+static ssize_t read_MSAT(OLE2* ole2, OLE2Header* oleh)
+{
+    // reconstitution of the MSAT
+    int count = (ole2->cfat < 109) ? ole2->cfat : 109;
+    if(count <= 0) {
+        if (xls_debug) fprintf(stderr, "Error: MSAT count out-of-bounds\n");
+        return -1;
+    }
+
+    ssize_t total_bytes_read = 0;
+    ssize_t bytes_read = 0;
+
+    ole2->SecID = ole_malloc(count*ole2->lsector);
+    ole2->SecIDCount = count*ole2->lsector/4;
+
+    if ((bytes_read = read_MSAT_header(ole2, oleh, count)) == -1) {
+        total_bytes_read = -1;
+        goto cleanup;
+    }
+    total_bytes_read += bytes_read;
+
+    if ((bytes_read = read_MSAT_body(ole2, total_bytes_read / ole2->lsector, count)) == -1) {
+        total_bytes_read = -1;
+        goto cleanup;
+    }
+    total_bytes_read += bytes_read;
+
+    if ((bytes_read = read_MSAT_trailer(ole2)) == -1) {
+        total_bytes_read = -1;
+        goto cleanup;
+    }
+    total_bytes_read += bytes_read;
+
+cleanup:
+    if (total_bytes_read == -1) {
+        if (ole2->SecID) {
+            free(ole2->SecID);
+            ole2->SecID = NULL;
+        }
+        if (ole2->SSecID) {
+            free(ole2->SSecID);
+            ole2->SSecID = NULL;
+        }
+    }
+
+    return total_bytes_read;
+}
--- r-cran-readxl-1.0.0.orig/src/xls.c
+++ r-cran-readxl-1.0.0/src/xls.c
@@ -41,7 +41,6 @@
 #include <sys/types.h>
 #include <string.h>
 #include <wchar.h>
-#include <assert.h>
 
 #include "libxls/endian.h"
 #include "libxls/xls.h"
@@ -50,31 +49,31 @@
 #define min(a,b) ((a) < (b) ? (a) : (b))
 #endif
 
-// #define DEBUG_DRAWINGS
+//#define DEBUG_DRAWINGS
 int xls_debug = 0;
 
 static double NumFromRk(DWORD_UA drk);
 static xls_formula_handler formula_handler;
 
-extern void xls_addSST(xlsWorkBook* pWB,SST* sst,DWORD size);
-extern void xls_appendSST(xlsWorkBook* pWB,BYTE* buf,DWORD size);
-extern void xls_addFormat(xlsWorkBook* pWB,FORMAT* format);
-extern BYTE* xls_addSheet(xlsWorkBook* pWB,BOUNDSHEET* bs);
-extern void xls_addRow(xlsWorkSheet* pWS,ROW* row);
-extern void xls_makeTable(xlsWorkSheet* pWS);
-extern struct st_cell_data *xls_addCell(xlsWorkSheet* pWS,BOF* bof,BYTE* buf);
-extern BYTE *xls_addFont(xlsWorkBook* pWB,FONT* font);
-extern void xls_addXF8(xlsWorkBook* pWB,XF8* xf);
-extern void xls_addXF5(xlsWorkBook* pWB,XF5* xf);
-extern void xls_addColinfo(xlsWorkSheet* pWS,COLINFO* colinfo);
-extern void xls_mergedCells(xlsWorkSheet* pWS,BOF* bof,BYTE* buf);
-extern void xls_parseWorkBook(xlsWorkBook* pWB);
-extern void xls_preparseWorkSheet(xlsWorkSheet* pWS);
-extern void xls_formatColumn(xlsWorkSheet* pWS);
-extern void xls_parseWorkSheet(xlsWorkSheet* pWS);
-extern void xls_dumpSummary(char *buf,int isSummary,xlsSummaryInfo	*pSI);
+extern xls_error_t xls_addSST(xlsWorkBook* pWB, SST* sst, DWORD size);
+extern xls_error_t xls_appendSST(xlsWorkBook* pWB, BYTE* buf, DWORD size);
+extern xls_error_t xls_addFormat(xlsWorkBook* pWB, FORMAT* format, DWORD size);
+extern char* xls_addSheet(xlsWorkBook* pWB, BOUNDSHEET* bs, DWORD size);
+extern xls_error_t xls_addRow(xlsWorkSheet* pWS,ROW* row);
+extern xls_error_t xls_makeTable(xlsWorkSheet* pWS);
+extern struct st_cell_data *xls_addCell(xlsWorkSheet* pWS, BOF* bof, BYTE* buf);
+extern char *xls_addFont(xlsWorkBook* pWB, FONT* font, DWORD size);
+extern xls_error_t xls_addXF8(xlsWorkBook* pWB, XF8* xf);
+extern xls_error_t xls_addXF5(xlsWorkBook* pWB, XF5* xf);
+extern xls_error_t xls_addColinfo(xlsWorkSheet* pWS, COLINFO* colinfo);
+extern xls_error_t xls_mergedCells(xlsWorkSheet* pWS, BOF* bof, BYTE* buf);
+extern xls_error_t xls_parseWorkBook(xlsWorkBook* pWB);
+extern xls_error_t xls_preparseWorkSheet(xlsWorkSheet* pWS);
+extern xls_error_t xls_formatColumn(xlsWorkSheet* pWS);
+extern xls_error_t xls_parseWorkSheet(xlsWorkSheet* pWS);
+extern void xls_dumpSummary(char *buf, int isSummary, xlsSummaryInfo *pSI);
 
-#if defined(_AIX) || defined(__sun)
+#ifdef AIX
 #pragma pack(1)
 #else
 #pragma pack(push, 1)
@@ -91,7 +90,7 @@ typedef struct {
 	uint32_t		os;
 	uint32_t		format[4];
 	uint32_t		count;
-	sectionList		secList[1];
+	sectionList		secList[0];
 } header;
 
 typedef struct {
@@ -102,12 +101,12 @@ typedef struct {
 typedef struct {
 	uint32_t		length;
 	uint32_t		numProperties;
-	propertyList	properties[1];
+	propertyList	properties[0];
 } sectionHeader;
 
 typedef struct {
 	uint32_t		propertyID;
-	uint32_t		data[1];
+	uint32_t		data[0];
 } property;
 
 #ifdef DEBUG_DRAWINGS
@@ -134,7 +133,7 @@ int xls(int debug)
     return 1;
 }
 
-void xls_addSST(xlsWorkBook* pWB,SST* sst,DWORD size)
+xls_error_t xls_addSST(xlsWorkBook* pWB,SST* sst,DWORD size)
 {
     verbose("xls_addSST");
 
@@ -145,18 +144,20 @@ void xls_addSST(xlsWorkBook* pWB,SST* ss
     pWB->sst.lastsz=0;
 
     pWB->sst.count = sst->num;
-    pWB->sst.string =(struct str_sst_string *)calloc(pWB->sst.count, sizeof(struct str_sst_string));
-    xls_appendSST(pWB,&sst->strings,size-8);
+    if ((pWB->sst.string = calloc(pWB->sst.count, sizeof(struct str_sst_string))) == NULL)
+        return LIBXLS_ERROR_MALLOC;
+
+    return xls_appendSST(pWB,&sst->strings,size-8);
 }
 
-void xls_appendSST(xlsWorkBook* pWB,BYTE* buf,DWORD size)
+xls_error_t xls_appendSST(xlsWorkBook* pWB, BYTE* buf, DWORD size)
 {
     DWORD ln;	// String character count
     DWORD ofs;	// Current offset in SST buffer
     DWORD rt;	// Count of rich text formatting runs
     DWORD sz;	// Size of asian phonetic settings block
     BYTE flag;	// String flags
-    BYTE* ret;
+    char* ret = NULL;
 
     if (xls_debug) {
 	    printf("xls_appendSST %u\n", size);
@@ -171,14 +172,14 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
 
         // Restore state when we're in a continue record
         // or read string length
-        if (pWB->sst.continued)
-        {
+        if (pWB->sst.continued) {
             ln=pWB->sst.lastln;
             rt=pWB->sst.lastrt;
             sz=pWB->sst.lastsz;
-        }
-        else
-        {
+        } else {
+            if (ofs + 2 > size) {
+                return LIBXLS_ERROR_PARSE;
+            }
             ln=xlsShortVal(*(WORD_UA *)(buf+ofs));
             rt = 0;
             sz = 0;
@@ -191,21 +192,27 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
 		}
 
         // Read flags
-        if ( (!pWB->sst.continued) || ( (pWB->sst.continued) && (ln != 0) ) )
-        {
+        if ( !pWB->sst.continued || (pWB->sst.continued && ln != 0) ) {
+            if (ofs + sizeof(BYTE) > size) {
+                return LIBXLS_ERROR_PARSE;
+            }
             flag=*(BYTE *)(buf+ofs);
             ofs++;
 
             // Count of rich text formatting runs
-            if (flag & 0x8)
-            {
+            if (flag & 0x8) {
+                if (ofs + sizeof(WORD_UA) > size) {
+                    return LIBXLS_ERROR_PARSE;
+                }
                 rt=xlsShortVal(*(WORD_UA *)(buf+ofs));
                 ofs+=2;
             }
 
             // Size of asian phonetic settings block
-            if (flag & 0x4)
-            {
+            if (flag & 0x4) {
+                if (ofs + sizeof(DWORD_UA) > size) {
+                    return LIBXLS_ERROR_PARSE;
+                }
                 sz=xlsIntVal(*(DWORD_UA *)(buf+ofs));
                 ofs+=4;
 
@@ -213,30 +220,26 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
 					printf("sz=%u\n", sz);
 				}
             }
-        }
-        else
-        {
+        } else {
             flag = 0;
         }
 
 		// Read characters (compressed or not)
         ln_toread = 0;
-        if (ln > 0)
-        {
-            if (flag & 0x1)
-            {
+        if (ln > 0) {
+            if (flag & 0x1) {
                 size_t new_len = 0;
                 ln_toread = min((size-ofs)/2, ln);
-                ret=unicode_decode(buf+ofs,ln_toread*2,&new_len,pWB->charset);
+                ret=unicode_decode((char *)buf+ofs,ln_toread*2,&new_len,pWB->charset);
 
                 if (ret == NULL)
                 {
-                    ret = (BYTE *)strdup("*failed to decode utf16*");
-                    new_len = strlen((char *)ret);
+                    ret = strdup("*failed to decode utf16*");
+                    new_len = strlen(ret);
                 }
 
-                ret = (BYTE *)realloc(ret,new_len+1);
-                *(BYTE*)(ret+new_len)=0;
+                ret = realloc(ret,new_len+1);
+                ret[new_len]=0;
 
                 ln -= ln_toread;
                 ofs+=ln_toread*2;
@@ -244,42 +247,44 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
                 if (xls_debug) {
 	                printf("String16SST: %s(%zd)\n",ret,new_len);
                 }
-            }
-            else
-            {
+            } else {
                 ln_toread = min((size-ofs), ln);
 
-				ret = utf8_decode((buf+ofs), ln_toread, pWB->charset);
+				ret = utf8_decode((char *)buf+ofs, ln_toread, pWB->charset);
 
                 ln  -= ln_toread;
-                ofs +=ln_toread;
+                ofs += ln_toread;
 
                 if (xls_debug) {
                 	printf("String8SST: %s(%u) \n",ret,ln);
                 }
             }
-        }
-        else
-        {
-         ret = (BYTE *)strdup("");
+        } else {
+            ret = strdup("");
         }
 
-        if (  (ln_toread > 0)
-            ||(!pWB->sst.continued) )
-        {
+        if (ln_toread > 0 || !pWB->sst.continued) {
             // Concat string if it's a continue, or add string in table
-            if (!pWB->sst.continued)
-            {
+            if (!pWB->sst.continued) {
+                if (pWB->sst.lastid >= pWB->sst.count) {
+                    free(ret);
+                    return LIBXLS_ERROR_PARSE;
+                }
                 pWB->sst.lastid++;
                 pWB->sst.string[pWB->sst.lastid-1].str=ret;
-            }
-            else
-            {
-                BYTE *tmp;
-                tmp=pWB->sst.string[pWB->sst.lastid-1].str;
-                tmp=(BYTE *)realloc(tmp,strlen((char *)tmp)+strlen((char *)ret)+1);
+            } else {
+                char *tmp = pWB->sst.string[pWB->sst.lastid-1].str;
+                if (tmp == NULL) {
+                    free(ret);
+                    return LIBXLS_ERROR_PARSE;
+                }
+                tmp = realloc(tmp, strlen(tmp)+strlen(ret)+1);
+                if (tmp == NULL)  {
+                    free(ret);
+                    return LIBXLS_ERROR_MALLOC;
+                }
                 pWB->sst.string[pWB->sst.lastid-1].str=tmp;
-                memcpy(tmp+strlen((char *)tmp),ret,strlen((char *)ret)+1);
+                memcpy(tmp+strlen(tmp), ret, strlen(ret)+1);
 				free(ret);
             }
 
@@ -289,22 +294,18 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
         }
 
 		// Jump list of rich text formatting runs
-        if (  (ofs < size)
-            &&(rt > 0) )
-          {
-           int rt_toread = min((size-ofs)/4, rt);
-           rt -= rt_toread;
-           ofs += rt_toread*4;
-          }
+        if (ofs < size && rt > 0) {
+            int rt_toread = min((size-ofs)/4, rt);
+            rt -= rt_toread;
+            ofs += rt_toread*4;
+        }
 
 		// Jump asian phonetic settings block
-        if (  (ofs < size)
-            &&(sz > 0) )
-          {
-           int sz_toread = min((size-ofs), sz);
-           sz -= sz_toread;
-           ofs += sz_toread;
-          }
+        if (ofs < size && sz > 0) {
+            int sz_toread = min((size-ofs), sz);
+            sz -= sz_toread;
+            ofs += sz_toread;
+        }
 
         pWB->sst.continued=0;
     }
@@ -320,6 +321,8 @@ void xls_appendSST(xlsWorkBook* pWB,BYTE
 			printf("continued: ln=%u, rt=%u, sz=%u\n", ln, rt, sz);
 		}
 	}
+
+    return LIBXLS_OK;
 }
 
 static double NumFromRk(DWORD_UA drk)
@@ -344,9 +347,9 @@ static double NumFromRk(DWORD_UA drk)
     return ret;
 }
 
-BYTE* xls_addSheet(xlsWorkBook* pWB, BOUNDSHEET *bs)
+char * xls_addSheet(xlsWorkBook* pWB, BOUNDSHEET *bs, DWORD size)
 {
-	BYTE* name;
+	char * name;
 	DWORD filepos;
 	BYTE visible, type;
 
@@ -356,7 +359,7 @@ BYTE* xls_addSheet(xlsWorkBook* pWB, BOU
 
 	// printf("charset=%s uni=%d\n", pWB->charset, unicode);
 	// printf("bs name %.*s\n", bs->name[0], bs->name+1);
-	name=get_string(bs->name, 0, pWB->is5ver, pWB->charset);
+	name = get_string(bs->name, size - sizeof(BOUNDSHEET), 0, pWB->is5ver, pWB->charset);
 	// printf("name=%s\n", name);
 
 	if(xls_debug) {
@@ -389,14 +392,10 @@ BYTE* xls_addSheet(xlsWorkBook* pWB, BOU
 		printf("   name: %s\n", name);
 	}
 
-    if (pWB->sheets.count==0)
-    {
-        pWB->sheets.sheet=(struct st_sheet_data *) malloc(sizeof (struct st_sheet_data));
-    }
-    else
-    {
-        pWB->sheets.sheet=(struct st_sheet_data *) realloc(pWB->sheets.sheet,(pWB->sheets.count+1)*sizeof (struct st_sheet_data));
-    }
+    pWB->sheets.sheet = realloc(pWB->sheets.sheet,(pWB->sheets.count+1)*sizeof (struct st_sheet_data));
+    if (pWB->sheets.sheet == NULL)
+        return NULL;
+
     pWB->sheets.sheet[pWB->sheets.count].name=name;
     pWB->sheets.sheet[pWB->sheets.count].filepos=filepos;
     pWB->sheets.sheet[pWB->sheets.count].visibility=visible;
@@ -407,12 +406,15 @@ BYTE* xls_addSheet(xlsWorkBook* pWB, BOU
 }
 
 
-void xls_addRow(xlsWorkSheet* pWS,ROW* row)
+xls_error_t xls_addRow(xlsWorkSheet* pWS,ROW* row)
 {
     struct st_row_data* tmp;
 
     //verbose ("xls_addRow");
 
+    if (row->index > pWS->rows.lastrow)
+        return LIBXLS_ERROR_PARSE;
+
     tmp=&pWS->rows.row[row->index];
     tmp->height=row->height;
     tmp->fcell=row->fcell;
@@ -421,15 +423,18 @@ void xls_addRow(xlsWorkSheet* pWS,ROW* r
     tmp->xf=row->xf&0xfff;
     tmp->xfflags=(row->xf >> 8)&0xf0;
     if(xls_debug) xls_showROW(tmp);
+
+    return LIBXLS_OK;
 }
 
-void xls_makeTable(xlsWorkSheet* pWS)
+xls_error_t xls_makeTable(xlsWorkSheet* pWS)
 {
     DWORD i,t;
     struct st_row_data* tmp;
     verbose ("xls_makeTable");
 
-    pWS->rows.row=(struct st_row_data *)calloc((pWS->rows.lastrow+1),sizeof(struct st_row_data));
+    if ((pWS->rows.row = calloc((pWS->rows.lastrow+1),sizeof(struct st_row_data))) == NULL)
+        return LIBXLS_ERROR_MALLOC;
 
 	// printf("ALLOC: rows=%d cols=%d\n", pWS->rows.lastrow, pWS->rows.lastcol);
     for (t=0;t<=pWS->rows.lastrow;t++)
@@ -440,7 +445,8 @@ void xls_makeTable(xlsWorkSheet* pWS)
         tmp->lcell=pWS->rows.lastcol;
 
 		tmp->cells.count = pWS->rows.lastcol+1;
-        tmp->cells.cell=(struct st_cell_data *)calloc(tmp->cells.count,sizeof(struct st_cell_data));
+        if ((tmp->cells.cell = calloc(tmp->cells.count,sizeof(struct st_cell_data))) == NULL)
+            return LIBXLS_ERROR_MALLOC;
 
         for (i=0;i<=pWS->rows.lastcol;i++)
         {
@@ -458,6 +464,7 @@ void xls_makeTable(xlsWorkSheet* pWS)
             tmp->cells.cell[i].str=NULL;
         }
     }
+    return LIBXLS_OK;
 }
 
 struct st_cell_data *xls_addCell(xlsWorkSheet* pWS,BOF* bof,BYTE* buf)
@@ -468,6 +475,9 @@ struct st_cell_data *xls_addCell(xlsWork
 
 	verbose ("xls_addCell");
 
+    if (bof->size < sizeof(COL))
+        return NULL;
+
 	// printf("ROW: %u COL: %u\n", xlsShortVal(((COL*)buf)->row), xlsShortVal(((COL*)buf)->col));
     row=&pWS->rows.row[xlsShortVal(((COL*)buf)->row)];
     //cell=&row->cells.cell[((COL*)buf)->col - row->fcell]; DFH - inconsistent
@@ -479,7 +489,9 @@ struct st_cell_data *xls_addCell(xlsWork
     {
     case XLS_RECORD_FORMULA:
     case XLS_RECORD_FORMULA_ALT:
-		// test for formula, if
+        if (bof->size < sizeof(FORMULA))
+            return NULL;
+
 		xlsConvertFormula((FORMULA *)buf);
         cell->id=XLS_RECORD_FORMULA;
         if (((FORMULA*)buf)->res!=0xffff) {
@@ -488,8 +500,8 @@ struct st_cell_data *xls_addCell(xlsWork
 			memcpy(&cell->d, &((FORMULA*)buf)->resid, sizeof(double));	// Required for ARM
 			cell->str=xls_getfcell(pWS->workbook,cell, NULL);
 		} else {
-			cell->l = 0xFFFF;
 			double d = ((FORMULA*)buf)->resdata[1];
+			cell->l = 0xFFFF;
 			switch(((FORMULA*)buf)->resid) {
 			case 0:		// String
 				break;	// cell is half complete, get the STRING next record
@@ -509,7 +521,8 @@ struct st_cell_data *xls_addCell(xlsWork
 		if(formula_handler) formula_handler(bof->id, bof->size, buf);
         break;
     case XLS_RECORD_MULRK:
-// printf("MULRK: %d\n", bof->size);
+        if (bof->size < sizeof(MULRK))
+            return NULL;
         for (i = 0; i < (bof->size - 6)/6; i++)	// 6 == 2 row + 2 col + 2 trailing index
         {
             cell=&row->cells.cell[xlsShortVal(((MULRK*)buf)->col + i)];
@@ -521,9 +534,16 @@ struct st_cell_data *xls_addCell(xlsWork
         }
         break;
     case XLS_RECORD_MULBLANK:
+        if (bof->size < sizeof(MULBLANK))
+            return NULL;
         for (i = 0; i < (bof->size - 6)/2; i++)	// 6 == 2 row + 2 col + 2 trailing index
         {
-            cell=&row->cells.cell[xlsShortVal(((MULBLANK*)buf)->col) + i];
+            WORD index = xlsShortVal(((MULBLANK*)buf)->col) + i;
+            if(index >= row->cells.count) {
+                if (xls_debug) fprintf(stderr, "Error: MULTI-BLANK index out of bounds\n");
+                return NULL;
+            }
+            cell=&row->cells.cell[index];
             cell->id=XLS_RECORD_BLANK;
             cell->xf=xlsShortVal(((MULBLANK*)buf)->xf[i]);
             cell->str=xls_getfcell(pWS->workbook,cell, NULL);
@@ -531,22 +551,32 @@ struct st_cell_data *xls_addCell(xlsWork
         break;
     case XLS_RECORD_LABELSST:
     case XLS_RECORD_LABEL:
-		cell->str=xls_getfcell(pWS->workbook,cell,(BYTE *)&((LABEL*)buf)->value);
-		sscanf((char *)cell->str, "%d", &cell->l);
-		sscanf((char *)cell->str, "%lf", &cell->d);
+        if (bof->size < sizeof(LABEL))
+            return NULL;
+		cell->str=xls_getfcell(pWS->workbook,cell,(WORD_UA *)&((LABEL*)buf)->value);
+        if (cell->str) {
+            sscanf((char *)cell->str, "%d", &cell->l);
+            sscanf((char *)cell->str, "%lf", &cell->d);
+        }
 		break;
     case XLS_RECORD_RK:
+        if (bof->size < sizeof(RK))
+            return NULL;
         cell->d=NumFromRk(xlsIntVal(((RK*)buf)->value));
         cell->str=xls_getfcell(pWS->workbook,cell, NULL);
         break;
     case XLS_RECORD_BLANK:
         break;
     case XLS_RECORD_NUMBER:
+        if (bof->size < sizeof(BR_NUMBER))
+            return NULL;
         xlsConvertDouble((BYTE *)&((BR_NUMBER*)buf)->value);
 		memcpy(&cell->d, &((BR_NUMBER*)buf)->value, sizeof(double)); // Required for ARM
         cell->str=xls_getfcell(pWS->workbook,cell, NULL);
         break;
     case XLS_RECORD_BOOLERR:
+        if (bof->size < sizeof(BOOLERR))
+            return NULL;
         cell->d = ((BOOLERR *)buf)->value;
         if (((BOOLERR *)buf)->iserror) {
             sprintf((char *)(cell->str = malloc(sizeof("error"))), "error");
@@ -563,21 +593,19 @@ struct st_cell_data *xls_addCell(xlsWork
 	return cell;
 }
 
-BYTE *xls_addFont(xlsWorkBook* pWB, FONT* font)
+char *xls_addFont(xlsWorkBook* pWB, FONT* font, DWORD size)
 {
     struct st_font_data* tmp;
 
     verbose("xls_addFont");
-    if (pWB->fonts.count==0)
-    {
-        pWB->fonts.font=(struct st_font_data *) malloc(sizeof(struct st_font_data));
-    } else {
-        pWB->fonts.font=(struct st_font_data *) realloc(pWB->fonts.font,(pWB->fonts.count+1)*sizeof(struct st_font_data));
-    }
+
+    pWB->fonts.font = realloc(pWB->fonts.font,(pWB->fonts.count+1)*sizeof(struct st_font_data));
+    if (pWB->fonts.font == NULL)
+        return NULL;
 
     tmp=&pWB->fonts.font[pWB->fonts.count];
 
-    tmp->name=get_string((BYTE*)&font->name, 0, pWB->is5ver, pWB->charset);
+    tmp->name = get_string(font->name, size - sizeof(FONT), 0, pWB->is5ver, pWB->charset);
 
     tmp->height=font->height;
     tmp->flag=font->flag;
@@ -594,38 +622,32 @@ BYTE *xls_addFont(xlsWorkBook* pWB, FONT
 	return tmp->name;
 }
 
-void xls_addFormat(xlsWorkBook* pWB, FORMAT* format)
+xls_error_t xls_addFormat(xlsWorkBook* pWB, FORMAT* format, DWORD size)
 {
     struct st_format_data* tmp;
 
     verbose("xls_addFormat");
-    if (pWB->formats.count==0)
-    {
-        pWB->formats.format=(struct st_format_data *) malloc(sizeof(struct st_format_data));
-    } else {
-        pWB->formats.format=(struct st_format_data *) realloc(pWB->formats.format,(pWB->formats.count+1)*sizeof(struct st_format_data));
-    }
-
-    tmp=&pWB->formats.format[pWB->formats.count];
-    tmp->index=format->index;
-    tmp->value=get_string(format->value, (BYTE)!pWB->is5ver, (BYTE)pWB->is5ver, pWB->charset);
+    pWB->formats.format = realloc(pWB->formats.format, (pWB->formats.count+1)*sizeof(struct st_format_data));
+    if (pWB->formats.format == NULL)
+        return LIBXLS_ERROR_MALLOC;
+
+    tmp = &pWB->formats.format[pWB->formats.count];
+    tmp->index = format->index;
+    tmp->value = get_string(format->value, size - sizeof(FORMAT), (BYTE)!pWB->is5ver, (BYTE)pWB->is5ver, pWB->charset);
     if(xls_debug) xls_showFormat(tmp);
     pWB->formats.count++;
+
+    return LIBXLS_OK;
 }
 
-void xls_addXF8(xlsWorkBook* pWB,XF8* xf)
+xls_error_t xls_addXF8(xlsWorkBook* pWB,XF8* xf)
 {
     struct st_xf_data* tmp;
 
     verbose("xls_addXF");
-    if (pWB->xfs.count==0)
-    {
-        pWB->xfs.xf=(struct st_xf_data *) malloc(sizeof(struct st_xf_data));
-    }
-    else
-    {
-        pWB->xfs.xf=(struct st_xf_data *) realloc(pWB->xfs.xf,(pWB->xfs.count+1)*sizeof(struct st_xf_data));
-    }
+    pWB->xfs.xf= realloc(pWB->xfs.xf, (pWB->xfs.count+1)*sizeof(struct st_xf_data));
+    if (pWB->xfs.xf == NULL)
+        return LIBXLS_ERROR_MALLOC;
 
     tmp=&pWB->xfs.xf[pWB->xfs.count];
 
@@ -642,20 +664,18 @@ void xls_addXF8(xlsWorkBook* pWB,XF8* xf
 
     //	xls_showXF(tmp);
     pWB->xfs.count++;
+
+    return LIBXLS_OK;
 }
-void xls_addXF5(xlsWorkBook* pWB,XF5* xf)
+
+xls_error_t xls_addXF5(xlsWorkBook* pWB,XF5* xf)
 {
     struct st_xf_data* tmp;
 
     verbose("xls_addXF");
-    if (pWB->xfs.count==0)
-    {
-        pWB->xfs.xf=(struct st_xf_data *) malloc(sizeof(struct st_xf_data));
-    }
-    else
-    {
-        pWB->xfs.xf=(struct st_xf_data *) realloc(pWB->xfs.xf,(pWB->xfs.count+1)*sizeof(struct st_xf_data));
-    }
+    pWB->xfs.xf = realloc(pWB->xfs.xf, (pWB->xfs.count+1)*sizeof(struct st_xf_data));
+    if (pWB->xfs.xf == NULL)
+        return LIBXLS_ERROR_MALLOC;
 
     tmp=&pWB->xfs.xf[pWB->xfs.count];
 
@@ -674,21 +694,17 @@ void xls_addXF5(xlsWorkBook* pWB,XF5* xf
 
     //	xls_showXF(tmp);
     pWB->xfs.count++;
+    return LIBXLS_OK;
 }
 
-void xls_addColinfo(xlsWorkSheet* pWS,COLINFO* colinfo)
+xls_error_t xls_addColinfo(xlsWorkSheet* pWS,COLINFO* colinfo)
 {
     struct st_colinfo_data* tmp;
 
     verbose("xls_addColinfo");
-    if (pWS->colinfo.count==0)
-    {
-        pWS->colinfo.col=(struct st_colinfo_data *) malloc(sizeof(struct st_colinfo_data));
-    }
-    else
-    {
-        pWS->colinfo.col=(struct st_colinfo_data *) realloc(pWS->colinfo.col,(pWS->colinfo.count+1)*sizeof(struct st_colinfo_data));
-    }
+    pWS->colinfo.col =  realloc(pWS->colinfo.col,(pWS->colinfo.count+1)*sizeof(struct st_colinfo_data));
+    if (pWS->colinfo.col == NULL)
+        return LIBXLS_ERROR_MALLOC;
 
     tmp=&pWS->colinfo.col[pWS->colinfo.count];
     tmp->first=colinfo->first;
@@ -699,19 +715,38 @@ void xls_addColinfo(xlsWorkSheet* pWS,CO
 
     if(xls_debug) xls_showColinfo(tmp);
     pWS->colinfo.count++;
+
+    return LIBXLS_OK;
 }
 
-void xls_mergedCells(xlsWorkSheet* pWS,BOF* bof,BYTE* buf)
+xls_error_t xls_mergedCells(xlsWorkSheet* pWS,BOF* bof,BYTE* buf)
 {
+    if (bof->size < sizeof(WORD_UA))
+        return LIBXLS_ERROR_PARSE;
+
     int count=xlsShortVal(*((WORD_UA *)buf));
+    DWORD limit = sizeof(WORD_UA)+count*sizeof(struct MERGEDCELLS);
+    if(limit > (DWORD)bof->size) {
+        verbose("Merged Cells Count out of range");
+        return LIBXLS_ERROR_PARSE;
+    }
     int i,c,r;
-    struct MERGEDCELLS* span;
+    struct MERGEDCELLS *span;
     verbose("Merged Cells");
     for (i=0;i<count;i++)
     {
         span=(struct MERGEDCELLS*)(buf+(2+i*sizeof(struct MERGEDCELLS)));
         xlsConvertMergedcells(span);
         //		printf("Merged Cells: [%i,%i] [%i,%i] \n",span->colf,span->rowf,span->coll,span->rowl);
+        // Sanity check:
+        if(!(   span->rowf <= span->rowl &&
+                span->rowl <= pWS->rows.lastrow &&
+                span->colf <= span->coll &&
+                span->coll <= pWS->rows.lastcol
+        )) {
+            return LIBXLS_ERROR_PARSE;
+        }
+
         for (r=span->rowf;r<=span->rowl;r++)
             for (c=span->colf;c<=span->coll;c++)
                 pWS->rows.row[r].cells.cell[c].isHidden=1;
@@ -719,40 +754,55 @@ void xls_mergedCells(xlsWorkSheet* pWS,B
         pWS->rows.row[span->rowf].cells.cell[span->colf].rowspan=(span->rowl-span->rowf+1);
         pWS->rows.row[span->rowf].cells.cell[span->colf].isHidden=0;
     }
+    return LIBXLS_OK;
 }
 
-void xls_parseWorkBook(xlsWorkBook* pWB)
+xls_error_t xls_parseWorkBook(xlsWorkBook* pWB)
 {
-    BOF bof1;
-    BOF bof2;
-    BYTE* buf;
-	BYTE once;
-
-	// this to prevent compiler warnings
-	once=0;
-	bof2.size = 0;
-	bof2.id = 0;
+    BOF bof1 = { 0 };
+    BOF bof2 = { 0 };
+    BYTE* buf = NULL;
+	BYTE once = 0;
+    xls_error_t retval = LIBXLS_OK;
+
     verbose ("xls_parseWorkBook");
-    do
-    {
+    do {
 		if(xls_debug > 10) {
 			printf("READ WORKBOOK filePos=%ld\n",  (long)pWB->filepos);
-			printf("  OLE: start=%d pos=%zd size=%zd fatPos=%zu\n", pWB->olestr->start, pWB->olestr->pos, pWB->olestr->size, pWB->olestr->fatpos);
+			printf("  OLE: start=%d pos=%zd size=%zd fatPos=%zu\n",
+                    pWB->olestr->start, pWB->olestr->pos, pWB->olestr->size, pWB->olestr->fatpos); 
 		}
 
-        ole2_read(&bof1, 1, 4, pWB->olestr);
+        if (ole2_read(&bof1, 1, 4, pWB->olestr) != 4) {
+            retval = LIBXLS_ERROR_READ;
+            goto cleanup;
+        }
         xlsConvertBof(&bof1);
  		if(xls_debug) xls_showBOF(&bof1);
 
-        buf=(BYTE *)malloc(bof1.size);
-        ole2_read(buf, 1, bof1.size, pWB->olestr);
+        if (bof1.size) {
+            if ((buf = realloc(buf, bof1.size)) == NULL) {
+                if (xls_debug) fprintf(stderr, "Error: failed to allocate buffer of size %d\n", (int)bof1.size);
+                retval = LIBXLS_ERROR_MALLOC;
+                goto cleanup;
+            }
+            if (ole2_read(buf, 1, bof1.size, pWB->olestr) != bof1.size) {
+                if (xls_debug) fprintf(stderr, "Error: failed to read OLE block\n");
+                retval = LIBXLS_ERROR_READ;
+                goto  cleanup;
+            }
+        }
 
         switch (bof1.id) {
         case XLS_RECORD_EOF:
             //verbose("EOF");
             break;
         case XLS_RECORD_BOF:	// BIFF5-8
-			{
+            if (bof1.size < sizeof(BIFF)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
+            {
 				BIFF *b = (BIFF*)buf;
                 xlsConvertBiff(b);
 				if (b->ver==0x600)
@@ -769,19 +819,29 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
             break;
 
         case XLS_RECORD_CODEPAGE:
+            if (bof1.size < sizeof(WORD_UA)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             pWB->codepage=xlsShortVal(*(WORD_UA *)buf);
 			if(xls_debug) printf("codepage=%x\n", pWB->codepage);
             break;
 
         case XLS_RECORD_CONTINUE:
 			if(once) {
-				if (bof2.id==XLS_RECORD_SST)
-					xls_appendSST(pWB,buf,bof1.size);
+				if (bof2.id==XLS_RECORD_SST) {
+					if ((retval = xls_appendSST(pWB,buf,bof1.size)) != LIBXLS_OK)
+                        goto cleanup;
+                }
 				bof1=bof2;
 			}
             break;
 
 		case XLS_RECORD_WINDOW1:
+            if (bof1.size < sizeof(WIND1)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			{
 				WIND1 *w = (WIND1*)buf;
                 xlsConvertWindow(w);
@@ -802,10 +862,16 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 			break;
 
         case XLS_RECORD_SST:
+            if (bof1.size < sizeof(SST)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			//printf("ADD SST\n");
 			//if(xls_debug) dumpbuf((BYTE *)"/tmp/SST",bof1.size,buf);
             xlsConvertSst((SST *)buf);
-            xls_addSST(pWB,(SST*)buf,bof1.size);
+            if ((retval = xls_addSST(pWB,(SST*)buf,bof1.size)) != LIBXLS_OK) {
+                goto cleanup;
+            }
             break;
 
         case XLS_RECORD_EXTSST:
@@ -813,23 +879,33 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
             break;
 
         case XLS_RECORD_BOUNDSHEET:
+            if (bof1.size < sizeof(BOUNDSHEET)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			{
 				//printf("ADD SHEET\n");
 				BOUNDSHEET *bs = (BOUNDSHEET *)buf;
                 xlsConvertBoundsheet(bs);
 				//char *s;
 				// different for BIFF5 and BIFF8
-				/*s = */ xls_addSheet(pWB,bs);
+				/*s = */ xls_addSheet(pWB, bs, bof1.size);
 			}
             break;
 
         case XLS_RECORD_XF:
 			if(pWB->is5ver) {
+                if (bof1.size < sizeof(XF5)) {
+                    retval = LIBXLS_ERROR_PARSE;
+                    goto cleanup;
+                }
 				XF5 *xf;
 				xf = (XF5 *)buf;
                 xlsConvertXf5(xf);
 
-				xls_addXF5(pWB,xf);
+				if ((retval = xls_addXF5(pWB,xf)) != LIBXLS_OK) {
+                    goto cleanup;
+                }
 				if(xls_debug) {
 					printf("   font: %d\n", xf->font);
 					printf(" format: %d\n", xf->format);
@@ -841,11 +917,18 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 					printf("linesty: %.4x\n", xf->linestyle);
 				}
 			} else {
+                if (bof1.size < sizeof(XF8)) {
+                    retval = LIBXLS_ERROR_PARSE;
+                    goto cleanup;
+                }
 				XF8 *xf;
 				xf = (XF8 *)buf;
                 xlsConvertXf8(xf);
 
-				xls_addXF8(pWB,xf);
+				if ((retval = xls_addXF8(pWB,xf)) != LIBXLS_OK) {
+                    goto cleanup;
+                }
+
 				if(xls_debug) {
 					xls_showXF(xf);
 				}
@@ -854,11 +937,15 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 
         case XLS_RECORD_FONT:
         case XLS_RECORD_FONT_ALT:
+            if (bof1.size < sizeof(FONT)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			{
-				BYTE *s;
+				char *s;
 				FONT *f = (FONT*)buf;
                 xlsConvertFont(f);
-				s = xls_addFont(pWB,f);
+				s = xls_addFont(pWB,f, bof1.size);
 				if(xls_debug) {
 					printf(" height: %d\n", f->height);
 					printf("   flag: 0x%x\n", f->flag);
@@ -874,8 +961,14 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 			break;
 
         case XLS_RECORD_FORMAT:
+            if (bof1.size < sizeof(FORMAT)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             xlsConvertFormat((FORMAT *)buf);
-            xls_addFormat(pWB,(FORMAT*)buf);
+            if ((retval = xls_addFormat(pWB, (FORMAT*)buf, bof1.size)) != LIBXLS_OK) {
+                goto cleanup;
+            }
             break;
 
 		case XLS_RECORD_STYLE:
@@ -888,7 +981,7 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 					printf("  ident: 0x%x\n", styl->ident);
 					printf("  level: 0x%x\n", styl->lvl);
 				} else {
-					BYTE *s = get_string(&buf[2], 1, pWB->is5ver, pWB->charset);
+					char *s = get_string((char *)&buf[2], bof1.size - 2, 1, pWB->is5ver, pWB->charset);
 					printf("  name=%s\n", s);
 				}
 			}
@@ -908,6 +1001,10 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 			break;
 
 		case XLS_RECORD_1904:
+            if (bof1.size < sizeof(BYTE)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			pWB->is1904 = *(BYTE *)buf;	// the field is a short, but with little endian the first byte is 0 or 1
 			if(xls_debug) {
 				printf("   mode: 0x%x\n", pWB->is1904);
@@ -916,8 +1013,9 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 		
 		case XLS_RECORD_DEFINEDNAME:
 			if(xls_debug) {
-				printf("DEFINEDNAME: ");
-				for(int i=0; i<bof1.size; ++i) printf("%2.2x ", buf[i]);
+				int i;
+                printf("   DEFINEDNAME: ");
+				for(i=0; i<bof1.size; ++i) printf("%2.2x ", buf[i]);
 				printf("\n");
 			}
 			break;
@@ -950,42 +1048,76 @@ void xls_parseWorkBook(xlsWorkBook* pWB)
 			}
             break;
         }
-		free(buf);
-
         bof2=bof1;
 		once=1;
     }
     while ((!pWB->olestr->eof)&&(bof1.id!=XLS_RECORD_EOF));
+
+cleanup:
+    if (buf)
+        free(buf);
+
+    return retval;
 }
 
 
-void xls_preparseWorkSheet(xlsWorkSheet* pWS)
+xls_error_t xls_preparseWorkSheet(xlsWorkSheet* pWS)
 {
     BOF tmp;
-    BYTE* buf;
+    BYTE* buf = NULL;
+    xls_error_t retval = LIBXLS_OK;
 
     verbose ("xls_preparseWorkSheet");
 
-    ole2_seek(pWS->workbook->olestr,pWS->filepos);
+    if (ole2_seek(pWS->workbook->olestr,pWS->filepos) == -1) {
+        retval = LIBXLS_ERROR_SEEK;
+        goto cleanup;
+    }
     do
     {
 		size_t read;
-        read = ole2_read(&tmp, 1,4,pWS->workbook->olestr);
-		assert(read == 4);
+		if((read = ole2_read(&tmp, 1, 4, pWS->workbook->olestr)) != 4) {
+            if (xls_debug) fprintf(stderr, "Error: failed to read OLE size\n");
+            retval = LIBXLS_ERROR_READ;
+            goto cleanup;
+        }
         xlsConvertBof(&tmp);
-        buf=(BYTE *)malloc(tmp.size);
-        read = ole2_read(buf, 1,tmp.size,pWS->workbook->olestr);
-		assert(read == tmp.size);
+        if (tmp.size) {
+            if ((buf = realloc(buf, tmp.size)) == NULL) {
+                if (xls_debug) fprintf(stderr, "Error: failed to allocate buffer of size %d\n", (int)tmp.size);
+                retval = LIBXLS_ERROR_MALLOC;
+                goto cleanup;
+            }
+            if((read = ole2_read(buf, 1, tmp.size, pWS->workbook->olestr)) != tmp.size) {
+                if (xls_debug) fprintf(stderr, "Error: failed to read OLE block\n");
+                retval = LIBXLS_ERROR_READ;
+                goto cleanup;
+            }
+        }
+
         switch (tmp.id)
         {
         case XLS_RECORD_DEFCOLWIDTH:
+            if (tmp.size < sizeof(WORD_UA)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             pWS->defcolwidth=xlsShortVal(*(WORD_UA *)buf)*256;
             break;
         case XLS_RECORD_COLINFO:
+            if (tmp.size < sizeof(COLINFO)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             xlsConvertColinfo((COLINFO*)buf);
-            xls_addColinfo(pWS,(COLINFO*)buf);
+            if ((retval = xls_addColinfo(pWS,(COLINFO*)buf)) != LIBXLS_OK)
+                goto cleanup;
             break;
         case XLS_RECORD_ROW:
+            if (tmp.size < sizeof(ROW)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             xlsConvertRow((ROW*)buf);
             if (pWS->rows.lastcol<((ROW*)buf)->lcell)
                 pWS->rows.lastcol=((ROW*)buf)->lcell;
@@ -995,12 +1127,20 @@ void xls_preparseWorkSheet(xlsWorkSheet*
         /* If the ROW record is incorrect or missing, infer the information from
          * cell data. */
         case XLS_RECORD_MULRK:
+            if (tmp.size < sizeof(MULRK)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             if (pWS->rows.lastcol<xlsShortVal(((MULRK*)buf)->col) + (tmp.size - 6)/6 - 1)
                 pWS->rows.lastcol=xlsShortVal(((MULRK*)buf)->col) + (tmp.size - 6)/6 - 1;
             if (pWS->rows.lastrow<xlsShortVal(((MULRK*)buf)->row))
                 pWS->rows.lastrow=xlsShortVal(((MULRK*)buf)->row);
             break;
         case XLS_RECORD_MULBLANK:
+            if (tmp.size < sizeof(MULBLANK)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             if (pWS->rows.lastcol<xlsShortVal(((MULBLANK*)buf)->col) + (tmp.size - 6)/2 - 1)
                 pWS->rows.lastcol=xlsShortVal(((MULBLANK*)buf)->col) + (tmp.size - 6)/2 - 1;
             if (pWS->rows.lastrow<xlsShortVal(((MULBLANK*)buf)->row))
@@ -1014,18 +1154,26 @@ void xls_preparseWorkSheet(xlsWorkSheet*
         case XLS_RECORD_FORMULA:
         case XLS_RECORD_FORMULA_ALT:
         case XLS_RECORD_BOOLERR:
+            if (tmp.size < sizeof(COL)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             if (pWS->rows.lastcol<xlsShortVal(((COL*)buf)->col))
                 pWS->rows.lastcol=xlsShortVal(((COL*)buf)->col);
             if (pWS->rows.lastrow<xlsShortVal(((COL*)buf)->row))
                 pWS->rows.lastrow=xlsShortVal(((COL*)buf)->row);
             break;
         }
-        free(buf);
     }
     while ((!pWS->workbook->olestr->eof)&&(tmp.id!=XLS_RECORD_EOF));
+
+cleanup:
+    if (buf)
+        free(buf);
+    return retval;
 }
 
-void xls_formatColumn(xlsWorkSheet* pWS)
+xls_error_t xls_formatColumn(xlsWorkSheet* pWS)
 {
     DWORD i,t,ii;
     DWORD fcol,lcol;
@@ -1051,28 +1199,42 @@ void xls_formatColumn(xlsWorkSheet* pWS)
             }
         }
     }
+    return LIBXLS_OK;
 }
 
-void xls_parseWorkSheet(xlsWorkSheet* pWS)
+xls_error_t xls_parseWorkSheet(xlsWorkSheet* pWS)
 {
     BOF tmp;
-    BYTE* buf;
+    BYTE* buf = NULL;
 	long offset = pWS->filepos;
-	// int continueRec = 0;
+    size_t read;
+    xls_error_t retval = 0;
+#ifdef DEBUG_DRAWINGS
+	int continueRec = 0;
+#endif
 
-	struct st_cell_data *cell;
+	struct st_cell_data *cell = NULL;
 	xlsWorkBook *pWB = pWS->workbook;
 
     verbose ("xls_parseWorkSheet");
 
-    xls_preparseWorkSheet(pWS);
+    if ((retval = xls_preparseWorkSheet(pWS)) != LIBXLS_OK) {
+        goto cleanup;
+    }
 	// printf("size=%d fatpos=%d)\n", pWS->workbook->olestr->size, pWS->workbook->olestr->fatpos);
 
-    xls_makeTable(pWS);
-    xls_formatColumn(pWS);
+    if ((retval = xls_makeTable(pWS)) != LIBXLS_OK) {
+        goto cleanup;
+    }
+
+    if ((retval = xls_formatColumn(pWS)) != LIBXLS_OK) {
+        goto cleanup;
+    }
 
-	cell = (void *)0;
-    ole2_seek(pWS->workbook->olestr,pWS->filepos);
+    if (ole2_seek(pWS->workbook->olestr,pWS->filepos) == -1) {
+        retval = LIBXLS_ERROR_SEEK;
+        goto cleanup;
+    }
     do
     {
 		long lastPos = offset;
@@ -1080,10 +1242,24 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 		if(xls_debug > 10) {
 			printf("LASTPOS=%ld pos=%zd filePos=%d filePos=%d\n", lastPos, pWB->olestr->pos, pWS->filepos, pWB->filepos);
 		}
-        ole2_read(&tmp, 1,4,pWS->workbook->olestr);
+		if((read = ole2_read(&tmp, 1, 4, pWS->workbook->olestr)) != 4) {
+            if (xls_debug) fprintf(stderr, "Error: failed to read OLE size\n");
+            retval = LIBXLS_ERROR_READ;
+            goto cleanup;
+        }
         xlsConvertBof((BOF *)&tmp);
-        buf=(BYTE *)malloc(tmp.size);
-        ole2_read(buf, 1,tmp.size,pWS->workbook->olestr);
+        if (tmp.size) {
+            if ((buf = realloc(buf, tmp.size)) == NULL) {
+                if (xls_debug) fprintf(stderr, "Error: failed to allocate buffer of size %d\n", (int)tmp.size);
+                retval = LIBXLS_ERROR_MALLOC;
+                goto cleanup;
+            }
+            if((read = ole2_read(buf, 1, tmp.size, pWS->workbook->olestr)) != tmp.size) {
+                if (xls_debug) fprintf(stderr, "Error: failed to read OLE block\n");
+                retval = LIBXLS_ERROR_READ;
+                goto cleanup;
+            }
+        }
 		offset += 4 + tmp.size;
 
 		if(xls_debug)
@@ -1094,17 +1270,33 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
         case XLS_RECORD_EOF:
             break;
         case XLS_RECORD_MERGEDCELLS:
-            xls_mergedCells(pWS,&tmp,buf);
+            if ((retval = xls_mergedCells(pWS,&tmp,buf)) != LIBXLS_OK) {
+                goto cleanup;
+            }
             break;
         case XLS_RECORD_ROW:
+            if (tmp.size < sizeof(ROW)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			if(xls_debug > 10) printf("ROW: %x at pos=%ld\n", tmp.id, lastPos);
             xlsConvertRow((ROW *)buf);
-            xls_addRow(pWS,(ROW*)buf);
+            if ((retval = xls_addRow(pWS,(ROW*)buf)) != LIBXLS_OK) {
+                goto cleanup;
+            }
             break;
 		case XLS_RECORD_DEFCOLWIDTH:
+            if (tmp.size < sizeof(WORD_UA)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			if(xls_debug > 10) printf("DEFAULT COL WIDTH: %d\n", *(WORD_UA *)buf);
 			break;
 		case XLS_RECORD_DEFAULTROWHEIGHT:
+            if (tmp.size < 2 * sizeof(WORD_UA)) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
 			if(xls_debug > 10) printf("DEFAULT ROW Height: 0x%x %d\n", ((WORD_UA *)buf)[0], ((WORD_UA *)buf)[1]);
 			break;
 		case XLS_RECORD_DBCELL:
@@ -1144,7 +1336,10 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
         case XLS_RECORD_LABEL:
         case XLS_RECORD_FORMULA:
         case XLS_RECORD_FORMULA_ALT:
-            cell = xls_addCell(pWS,&tmp,buf);
+            if ((cell = xls_addCell(pWS, &tmp, buf)) == NULL) {
+                retval = LIBXLS_ERROR_PARSE;
+                goto cleanup;
+            }
             break;
 		case XLS_RECORD_ARRAY:
 			if(formula_handler) formula_handler(tmp.id, tmp.size, buf);
@@ -1152,7 +1347,7 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 
 		case XLS_RECORD_STRING:
 			if(cell && (cell->id == XLS_RECORD_FORMULA || cell->id == XLS_RECORD_FORMULA_ALT)) {
-				cell->str = get_string(buf, (BYTE)!pWB->is5ver, pWB->is5ver, pWB->charset);
+				cell->str = get_string((char *)buf, tmp.size, (BYTE)!pWB->is5ver, pWB->is5ver, pWB->charset);
 				if (xls_debug) xls_showCell(cell);
 			}
 			break;
@@ -1193,7 +1388,7 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 			sheetOffset = 100;
 			unsigned int total = tmp.size;
 			unsigned int off = 0;
-
+			
 			while(off < total) {
 				struct drawHeader fooper  = drawProc(buf, total, &off, 0);
 				(void)fooper;
@@ -1204,9 +1399,9 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 			if(formFunc) printf("%s\n", formFunc);
 			free(formData), formData = NULL;
 			free(formFunc), formFunc = NULL;
-
+			
 		}	break;
-
+		
 		case XLS_RECORD_TXO:
 		{
 			struct {
@@ -1219,7 +1414,8 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 				uint16_t	reserved2;
 			} foo;
 			memcpy(&foo, buf, 18);
-			printf("TXO: grbit=0x%4.4X rot=0x%4.4X chText=0x%4.4X cbRuns=0x%4.4X ifntEmpty=0x%X reserved2=0x%X\n", foo.grbit, foo.rot, foo.cchText, foo.cbRuns, foo.ifntEmpty, foo.reserved2);
+			printf("TXO: grbit=0x%4.4X rot=0x%4.4X chText=0x%4.4X cbRuns=0x%4.4X ifntEmpty=0x%X reserved2=0x%X\n",
+                    foo.grbit, foo.rot, foo.cchText, foo.cbRuns, foo.ifntEmpty, foo.reserved2);
 			
 			printf("Res1: ");
 			for(int i=0; i<6; ++i) printf("%2.2x ", foo.reserved1[i]);
@@ -1274,7 +1470,8 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 			int len = (int)(tmp.size - sizeof(foo));
 			int off = sizeof(foo);
 			
-			printf("OBJ ft=0x%X cb=0x%X ot=0x%X idx=0x%X flags=0x%X len=%d ", foo.ft, foo.cb, foo.ot, foo.idx, foo.flags, (int)(tmp.size - sizeof(foo)) );
+			printf("OBJ ft=0x%X cb=0x%X ot=0x%X idx=0x%X flags=0x%X len=%d ",
+                    foo.ft, foo.cb, foo.ot, foo.idx, foo.flags, (int)(tmp.size - sizeof(foo)) );
 			//for(int i=0; i<6; ++i) printf(" 0x%02.2x", foo.unused[i]);
 			printf("\n");
 			
@@ -1334,7 +1531,8 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 				uint8_t		strType;
 			} note;
 			memcpy(&note, buf, sizeof(note));
-			printf("NOTE: row=%d col=%d flags=0x%x idx=%d strLen=%d strType=%d :  ", note.row, note.col, note.flags, note.idx, note.strLen, note.strType);
+			printf("   NOTE: row=%d col=%d flags=0x%x idx=%d strLen=%d strType=%d :  ",
+                    note.row, note.col, note.flags, note.idx, note.strLen, note.strType);
 			for(int i=0; i<note.strLen; ++i) printf("%2.2x ", buf[i+sizeof(note)]);
 			printf("\n  %.*s now at %ld len=%d\n", note.strLen, buf + sizeof(note), sizeof(note)+note.strLen, tmp.size);
 
@@ -1344,59 +1542,72 @@ void xls_parseWorkSheet(xlsWorkSheet* pW
 #endif
 
         default:
-		  // printBOF:
+#ifdef DEBUG_DRAWINGS
+		  printBOF:
+#endif
 			if(xls_debug)
 			{
 				//xls_showBOF(&tmp);
-				printf("   [%d:%d]: 0x%X at pos=%lu size=%u\n", xlsShortVal(((COL*)buf)->row), xlsShortVal(((COL*)buf)->col), tmp.id, lastPos, tmp.size);
+                if (tmp.size >= sizeof(COL)) {
+                    printf("   [%d:%d]: 0x%X at pos=%lu size=%u\n", xlsShortVal(((COL*)buf)->row), xlsShortVal(((COL*)buf)->col),
+                            tmp.id, lastPos, tmp.size);
+                } else {
+                    printf("   0x%X at pos=%lu size=%u\n", tmp.id, lastPos, tmp.size);
+                }
 			}
             break;
         }
-        free(buf);
     }
     while ((!pWS->workbook->olestr->eof)&&(tmp.id!=XLS_RECORD_EOF));
+
+cleanup:
+    if (buf)
+        free(buf);
+
+    return retval;
 }
 
 xlsWorkSheet * xls_getWorkSheet(xlsWorkBook* pWB,int num)
 {
-    xlsWorkSheet * pWS;
+    xlsWorkSheet * pWS = NULL;
     verbose ("xls_getWorkSheet");
-    pWS=(xlsWorkSheet *)calloc(1, sizeof(xlsWorkSheet));
-    pWS->filepos=pWB->sheets.sheet[num].filepos;
-    pWS->workbook=pWB;
-    pWS->rows.lastcol=0;
-    pWS->rows.lastrow=0;
-    pWS->colinfo.count=0;
-    return(pWS);
+    if (num >= 0 && num < pWB->sheets.count) {
+        pWS = calloc(1, sizeof(xlsWorkSheet));
+        pWS->filepos=pWB->sheets.sheet[num].filepos;
+        pWS->workbook=pWB;
+        pWS->rows.lastcol=0;
+        pWS->rows.lastrow=0;
+        pWS->colinfo.count=0;
+    }
+    return pWS;
 }
 
-xlsWorkBook* xls_open(const char *file,const char* charset)
-{
+static xlsWorkBook *xls_open_ole(OLE2 *ole, const char *charset, xls_error_t *outError) {
     xlsWorkBook* pWB;
-    OLE2*		ole;
-
-    pWB=(xlsWorkBook*)calloc(1, sizeof(xlsWorkBook));
-    verbose ("xls_open");
+    xls_error_t retval = LIBXLS_OK;
 
-    // open excel file
-    if (!(ole=ole2_open((BYTE *)file)))
-    {
-        if(xls_debug) printf("File \"%s\" not found\n",file);
-		free(pWB);
-        return(NULL);
-    }
+    pWB = calloc(1, sizeof(xlsWorkBook));
+    verbose ("xls_open_ole");
 
-    if ((pWB->olestr=ole2_fopen(ole, (BYTE *)"\005SummaryInformation")))
+    if ((pWB->olestr=ole2_fopen(ole, "\005SummaryInformation")))
     {
         pWB->summary = calloc(1,4096);
-		ole2_read(pWB->summary, 4096, 1, pWB->olestr);
+		if (ole2_read(pWB->summary, 4096, 1, pWB->olestr) == -1) {
+            if (xls_debug) fprintf(stderr, "SummaryInformation not found\n");
+            retval = LIBXLS_ERROR_READ;
+            goto cleanup;
+        }
 		ole2_fclose(pWB->olestr);
 	}
 
-    if ((pWB->olestr=ole2_fopen(ole, (BYTE *)"\005DocumentSummaryInformation")))
+    if ((pWB->olestr=ole2_fopen(ole, "\005DocumentSummaryInformation")))
     {
-        pWB->docSummary = calloc(1,4096);
-		ole2_read(pWB->docSummary, 4096, 1, pWB->olestr);
+        pWB->docSummary = calloc(1, 4096);
+		if (ole2_read(pWB->docSummary, 4096, 1, pWB->olestr) == -1) {
+            if (xls_debug) fprintf(stderr, "DocumentSummaryInformation not found\n");
+            retval = LIBXLS_ERROR_READ;
+            goto cleanup;
+        }
 		ole2_fclose(pWB->olestr);
 	}
 
@@ -1418,23 +1629,67 @@ xlsWorkBook* xls_open(const char *file,c
 #endif
 
     // open Workbook
-    if (!(pWB->olestr=ole2_fopen(ole,(BYTE *)"Workbook")) && !(pWB->olestr=ole2_fopen(ole,(BYTE *)"Book")))
+    if (!(pWB->olestr=ole2_fopen(ole,"Workbook")) && !(pWB->olestr=ole2_fopen(ole,"Book")))
     {
-        if(xls_debug) printf("Workbook not found\n");
-        ole2_close(ole);
-		free(pWB);
-        return(NULL);
+        if(xls_debug) fprintf(stderr, "Workbook not found\n");
+        retval = LIBXLS_ERROR_PARSE;
+        goto cleanup;
     }
 
-
     pWB->sheets.count=0;
     pWB->xfs.count=0;
     pWB->fonts.count=0;
-    pWB->charset = (char *)malloc(strlen(charset) * sizeof(char)+1);
-    strcpy(pWB->charset, charset);
-    xls_parseWorkBook(pWB);
+    if (charset) {
+        pWB->charset = malloc(strlen(charset) * sizeof(char)+1);
+        strcpy(pWB->charset, charset);
+    } else {
+        pWB->charset = strdup("UTF-8");
+    }
+
+    retval = xls_parseWorkBook(pWB);
 
-    return(pWB);
+cleanup:
+    if (retval != LIBXLS_OK) {
+        if (!pWB->olestr)
+            ole2_close(ole);
+        xls_close_WB(pWB);
+        pWB = NULL;
+    }
+    if (outError)
+        *outError = retval;
+
+    return pWB;
+}
+
+xlsWorkBook* xls_open(const char *file, const char* charset)
+{
+    return xls_open_file(file, charset, NULL);
+}
+
+xlsWorkBook* xls_open_file(const char *file, const char* charset, xls_error_t *outError) {
+    OLE2* ole = NULL;
+
+    if (!(ole=ole2_open_file(file)))
+    {
+        if (xls_debug) fprintf(stderr, "File \"%s\" not found\n",file);
+        if (outError) *outError = LIBXLS_ERROR_OPEN;
+        return NULL;
+    }
+
+    return xls_open_ole(ole, charset, outError);
+}
+
+xlsWorkBook *xls_open_buffer(const unsigned char *buffer, size_t len,
+        const char *charset, xls_error_t *outError) {
+    OLE2* ole = NULL;
+
+    if (!(ole=ole2_open_buffer(buffer, len)))
+    {
+        if (outError) *outError = LIBXLS_ERROR_OPEN;
+        return NULL;
+    }
+
+    return xls_open_ole(ole, charset, outError);
 }
 
 xlsRow *xls_row(xlsWorkSheet* pWS, WORD cellRow)
@@ -1453,7 +1708,7 @@ xlsCell	*xls_cell(xlsWorkSheet* pWS, WOR
 
     if(cellRow > pWS->rows.lastrow) return NULL;
     row = &pWS->rows.row[cellRow];
-    if(cellCol > row->lcell) return NULL;
+    if(cellCol >= row->lcell) return NULL;
 
     return &row->cells.cell[cellCol];
 }
@@ -1467,11 +1722,11 @@ void xls_close_WB(xlsWorkBook* pWB)
 	if(!pWB) return;
 
     // OLE first
-	ole=pWB->olestr->ole;
-	
-	ole2_fclose(pWB->olestr);
-
-	ole2_close(ole);
+    if (pWB->olestr) {
+        ole=pWB->olestr->ole;
+        ole2_fclose(pWB->olestr);
+        ole2_close(ole);
+    }
 
     // WorkBook
     free(pWB->charset);
@@ -1555,6 +1810,23 @@ const char* xls_getVersion(void)
     return PACKAGE_VERSION;
 }
 
+const char* xls_getError(xls_error_t code) {
+    if (code == LIBXLS_OK)
+        return "No error";
+    if (code == LIBXLS_ERROR_READ)
+        return "Unable to read from file";
+    if (code == LIBXLS_ERROR_OPEN)
+        return "Unable to open file";
+    if (code == LIBXLS_ERROR_SEEK)
+        return "Unable to seek within file";
+    if (code == LIBXLS_ERROR_MALLOC)
+        return "Unable to allocate memory";
+    if (code == LIBXLS_ERROR_PARSE)
+        return "Unable to parse file";
+
+    return "Unknown error";
+}
+
 //
 // http://poi.apache.org/hpsf/internals.html
 // or google "DocumentSummaryInformation and UserDefined Property Sets" and look for MSDN hits
--- r-cran-readxl-1.0.0.orig/src/xlstool.c
+++ r-cran-readxl-1.0.0/src/xlstool.c
@@ -57,8 +57,6 @@
 
 extern int xls_debug;
 
-// static void xls_showBOUNDSHEET(void* bsheet);
-
 static const DWORD colors[] =
     {
         0x000000,
@@ -119,77 +117,6 @@ static const DWORD colors[] =
         0x333333
     };
 
-#if HAVE_ASPRINTF != 1
-
-#include <stdarg.h>
-
-#ifdef MSDN
-static int asprintf(char **ret, const char *format, ...)
-{
-	int i, size=100;
-    char *p, *np;
-
-	va_list ap;
-
-	if ((p = (char *)malloc(size)) == NULL)
-        return -1;
-
-    while (1) {
-	    va_start(ap, format); 
-
-	    i = _vsnprintf(p, size, format, ap);
-
-	    va_end(ap);
-
-        if (i > -1 && i < size)
-        {
-            i++;
-            break;
-        }
-
-        if (i > -1)     /* glibc 2.1 */
-            size = i+1; /* precisely what is needed */
-        else            /* glibc 2.0 */
-            size *= 2;  /* twice the old size */
-
-        if ((np = realloc (p, size)) == NULL) {
-            free(p);
-            return -1;
-        } else {
-            p = np;
-        }
-    }
-
-	*ret = p;
-	return i > 255 ? 255 : i;
-}
-
-#else
-
-static int asprintf(char **ret, const char *format, ...)
-{
-	int i;
-	char *str;
-
-	va_list ap;
-
-	va_start(ap, format); 
-	i = vsnprintf(NULL, 0, format, ap) + 1;
-	va_end(ap);
-
-	str = (char *)malloc(i);
-
-	va_start(ap, format);
-	i = vsnprintf(str, i, format, ap);
-	va_end(ap);
-
-	*ret = str;
-	return i > 255 ? 255 : i;
-}
-#endif
-
-#endif
-
 
 void dumpbuf(BYTE* fname,long size,BYTE* buf)
 {
@@ -206,10 +133,10 @@ void verbose(char* str)
         printf("libxls : %s\n",str);
 }
 
-BYTE *utf8_decode(BYTE *str, DWORD len, char *encoding)
+char *utf8_decode(const char *str, DWORD len, char *encoding)
 {
 	int utf8_chars = 0;
-	BYTE *ret;
+	char *ret = NULL;
     DWORD i;
 	
 	for(i=0; i<len; ++i) {
@@ -219,14 +146,14 @@ BYTE *utf8_decode(BYTE *str, DWORD len,
 	}
 	
 	if(utf8_chars == 0 || strcmp(encoding, "UTF-8")) {
-		ret = (BYTE *)malloc(len+1);
+		ret = malloc(len+1);
 		memcpy(ret, str, len);
 		ret[len] = 0;
 	} else {
         DWORD i;
-        BYTE *out;
+        char *out;
 		// UTF-8 encoding inline
-		ret = (BYTE *)malloc(len+utf8_chars+1);
+		ret = malloc(len+utf8_chars+1);
 		out = ret;
 		for(i=0; i<len; ++i) {
 			BYTE c = str[i];
@@ -244,16 +171,16 @@ BYTE *utf8_decode(BYTE *str, DWORD len,
 }
 
 // Convert unicode string to to_enc encoding
-BYTE* unicode_decode(const BYTE *s, int len, size_t *newlen, const char* to_enc)
+char* unicode_decode(const char *s, size_t len, size_t *newlen, const char* to_enc)
 {
 #ifdef HAVE_ICONV
 	// Do iconv conversion
-#if defined(_AIX) || defined(__sun)
+#ifdef AIX
     const char *from_enc = "UTF-16le";
 #else
     const char *from_enc = "UTF-16LE";
 #endif
-    BYTE* outbuf = 0;
+    char* outbuf = 0;
 
     if(s && len && from_enc && to_enc)
     {
@@ -261,8 +188,8 @@ BYTE* unicode_decode(const BYTE *s, int
         int outlen = len;
         size_t inlenleft = len;
         iconv_t ic = iconv_open(to_enc, from_enc);
-        BYTE* src_ptr = (BYTE*) s;
-        BYTE* out_ptr = 0;
+        const char* src_ptr = s;
+        char* out_ptr = 0;
 
         if(ic == (iconv_t)-1)
         {
@@ -286,18 +213,14 @@ BYTE* unicode_decode(const BYTE *s, int
             }
         }
         size_t st; 
-        outbuf = (BYTE*)malloc(outlen + 1);
+        outbuf = malloc(outlen + 1);
 
 		if(outbuf)
         {
-            out_ptr = (BYTE*)outbuf;
+            out_ptr = outbuf;
             while(inlenleft)
             {
-#ifdef _WIN32
-                st = iconv(ic, (const char **)&src_ptr, &inlenleft, (char **)&out_ptr,(size_t *) &outlenleft);
-#else
                 st = iconv(ic, (char **)&src_ptr, &inlenleft, (char **)&out_ptr,(size_t *) &outlenleft);
-#endif
                 if(st == (size_t)(-1))
                 {
                     if(errno == E2BIG)
@@ -305,7 +228,7 @@ BYTE* unicode_decode(const BYTE *s, int
                         size_t diff = out_ptr - outbuf;
                         outlen += inlenleft;
                         outlenleft += inlenleft;
-                        outbuf = (BYTE*)realloc(outbuf, outlen + 1);
+                        outbuf = realloc(outbuf, outlen + 1);
                         if(!outbuf)
                         {
                             break;
@@ -346,18 +269,19 @@ BYTE* unicode_decode(const BYTE *s, int
 
     x=(short *)s;
 
-    w = (wchar_t*)malloc((len+1)*sizeof(wchar_t));
+    w = malloc((len/2+1)*sizeof(wchar_t));
 
-    for(i=0; i<len; i++)
+    for(i=0; i<len/2; i++)
     {
         w[i]=xlsShortVal(x[i]);
     }
-    w[len] = '\0';
+    w[len/2] = '\0';
 
     count = wcstombs(NULL, w, 0);
 
 	if (count <= 0) {
 		if (newlen) *newlen = 0;
+		free(w);
 		return NULL;
 	}
 
@@ -365,7 +289,7 @@ BYTE* unicode_decode(const BYTE *s, int
 	count2 = wcstombs(converted, w, count);
     free(w);
 	if (count2 <= 0) {
-		printf("wcstombs failed (%d)\n", len);
+		printf("wcstombs failed (%d)\n", len/2);
 		if (newlen) *newlen = 0;
 		return converted;
 	} else {
@@ -376,52 +300,59 @@ BYTE* unicode_decode(const BYTE *s, int
 }
 
 // Read and decode string
-BYTE* get_string(BYTE *s, BYTE is2, BYTE is5ver, char *charset)
+char *get_string(const char *s, size_t len, BYTE is2, BYTE is5ver, char *charset)
 {
     WORD ln;
-    DWORD ofs;
-    BYTE flag;
-    BYTE* str;
-    BYTE* ret;
+    DWORD ofs = 0;
+    BYTE flag = 0;
+    const char *str = s;
+    char *ret = NULL;
 	
-	flag = 0;
-    str=s;
-
-    ofs=0;
-
     if (is2) {
 		// length is two bytes
+        if (ofs + 2 > len) {
+            return NULL;
+        }
         ln=xlsShortVal(*(WORD_UA *)str);
         ofs+=2;
     } else {
 		// single byte length
+        if (ofs + 1 > len) {
+            return NULL;
+        }
         ln=*(BYTE*)str;
         ofs++;
     }
 
 	if(!is5ver) {
 		// unicode strings have a format byte before the string
+        if (ofs + 1 > len) {
+            return NULL;
+        }
 		flag=*(BYTE*)(str+ofs);
 		ofs++;
 	}
-    if (flag&0x8)
-    {
+    if (flag&0x8) {
 		// WORD rt;
         // rt=*(WORD*)(str+ofs); // unused
         ofs+=2;
     }
-    if (flag&0x4)
-    {
+    if (flag&0x4) {
 		// DWORD sz;
         // sz=*(DWORD*)(str+ofs); // unused
         ofs+=4;
     }
-    if(flag & 0x1)
-    {
+    if(flag & 0x1) {
+        if (ofs + 2*ln > len) {
+            return NULL;
+        }
 		size_t new_len = 0;
         ret = unicode_decode(str+ofs,ln*2, &new_len,charset);
     } else {
-		ret = utf8_decode((str+ofs), ln, charset);
+        if (ofs + ln > len) {
+            return NULL;
+        }
+		ret = utf8_decode(str+ofs, ln, charset);
     }
 
 #if 0	// debugging
@@ -628,78 +559,89 @@ void xls_showXF(XF8* xf)
     printf("GroundColor: 0x%x\n",xf->groundcolor);
 }
 
-BYTE *xls_getfcell(xlsWorkBook* pWB,struct st_cell_data* cell,BYTE *label)
+char *xls_getfcell(xlsWorkBook* pWB, struct st_cell_data* cell, WORD *label)
 {
-    struct st_xf_data *xf;
-	WORD	len;
+    struct st_xf_data *xf = NULL;
+	WORD	len = 0;
+    WORD    offset = 0;
     char	*ret = NULL;
+    size_t  retlen = 100;
 
-    xf=&pWB->xfs.xf[cell->xf];
+    if (cell->xf < pWB->xfs.count)
+        xf=&pWB->xfs.xf[cell->xf];
 
     switch (cell->id)
     {
     case XLS_RECORD_LABELSST:
-		//printf("WORD: %u short: %u str: %s\n", *(DWORD_UA *)label, xlsIntVal(*(DWORD_UA *)label), pWB->sst.string[xlsIntVal(*(DWORD_UA *)label)].str );
-        asprintf(&ret,"%s",pWB->sst.string[xlsIntVal(*(DWORD_UA *)label)].str);
+		//printf("WORD: %u short: %u str: %s\n", *label, xlsShortVal(*label), pWB->sst.string[xlsIntVal(*label)].str );
+        offset = xlsIntVal(*(DWORD *)label);
+        if(offset < pWB->sst.count && pWB->sst.string[offset].str) {
+            ret = strdup(pWB->sst.string[offset].str);
+        }
         break;
     case XLS_RECORD_BLANK:
     case XLS_RECORD_MULBLANK:
-        asprintf(&ret, "%s", "");
+        ret = strdup("");
         break;
     case XLS_RECORD_LABEL:
 		len = xlsShortVal(*label);
-        label += 2;
+        label++;
 		if(pWB->is5ver) {
-			asprintf(&ret,"%.*s", len, (char *)label);
+            ret = malloc(len+1);
+            memcpy(ret, label, len);
+            ret[len] = 0;
 			//printf("Found BIFF5 string of len=%d \"%s\"\n", len, ret);
-		} else
-		if ((*(BYTE *)label & 0x01) == 0) {
-			ret = (char *)utf8_decode((BYTE *)label + 1, len, pWB->charset);
+		} else if ((*(BYTE *)label & 0x01) == 0) {
+			ret = utf8_decode((char *)label + 1, len, pWB->charset);
 		} else {
 			size_t newlen;
-		    ret = (char *)unicode_decode((BYTE *)label + 1, len*2, &newlen, pWB->charset);
+		    ret = unicode_decode((char *)label + 1, len*2, &newlen, pWB->charset);
 		}
         break;
     case XLS_RECORD_RK:
     case XLS_RECORD_NUMBER:
-        asprintf(&ret,"%lf", cell->d);
+        ret = malloc(retlen);
+        snprintf(ret, retlen, "%lf", cell->d);
 		break;
 		//		if( RK || MULRK || NUMBER || FORMULA)
 		//		if (cell->id==0x27e || cell->id==0x0BD || cell->id==0x203 || 6 (formula))
     default:
-        switch (xf->format)
-        {
-        case 0:
-            asprintf(&ret,"%d",(int)cell->d);
-            break;
-        case 1:
-            asprintf(&ret,"%d",(int)cell->d);
-            break;
-        case 2:
-            asprintf(&ret,"%.1f",cell->d);
-            break;
-        case 9:
-            asprintf(&ret,"%d",(int)cell->d);
-            break;
-        case 10:
-            asprintf(&ret,"%.2f",cell->d);
-            break;
-        case 11:
-            asprintf(&ret,"%.1e",cell->d);
-            break;
-        case 14:
-			//ret=ctime(cell->d);
-			asprintf(&ret,"%.0f",cell->d);
-            break;
-        default:
-			// asprintf(&ret,"%.4.2f (%i)",cell->d,xf->format);break;
-            asprintf(&ret,"%.2f",cell->d);
+        if (xf) {
+            ret = malloc(retlen);
+            switch (xf->format)
+            {
+                case 0:
+                    snprintf(ret, retlen, "%d", (int)cell->d);
+                    break;
+                case 1:
+                    snprintf(ret, retlen, "%d", (int)cell->d);
+                    break;
+                case 2:
+                    snprintf(ret, retlen, "%.1f", cell->d);
+                    break;
+                case 9:
+                    snprintf(ret, retlen, "%d", (int)cell->d);
+                    break;
+                case 10:
+                    snprintf(ret, retlen, "%.2f", cell->d);
+                    break;
+                case 11:
+                    snprintf(ret, retlen, "%.1e", cell->d);
+                    break;
+                case 14:
+                    //ret=ctime(cell->d);
+                    snprintf(ret, retlen, "%.0f", cell->d);
+                    break;
+                default:
+                    // asprintf(&ret,"%.4.2f (%i)",cell->d,xf->format);break;
+                    snprintf(ret, retlen, "%.2f", cell->d);
+                    break;
+            }
             break;
         }
-        break;
     }
 
-    return (BYTE *)ret;
+    return ret;
 }
 
 char* xls_getCSS(xlsWorkBook* pWB)
