Index: E:/TEMP/lotus_/boot.c
===================================================================
--- E:/TEMP/lotus_/boot.c	(revision 1)
+++ E:/TEMP/lotus_/boot.c	(revision 4)
@@ -13,6 +13,8 @@
 #include "lotus-formula.h"
 #include "lotus-types.h"
 
+#include "works.h"
+
 #include <goffice/goffice.h>
 #include <workbook-view.h>
 #include <gnm-plugin.h>
@@ -23,9 +25,9 @@
 
 GNM_PLUGIN_MODULE_HEADER;
 
-gboolean lotus_file_probe (GOFileOpener const *fo, GsfInput *input,
+G_MODULE_EXPORT gboolean lotus_file_probe (GOFileOpener const *fo, GsfInput *input,
                            GOFileProbeLevel pl);
-void     lotus_file_open (GOFileOpener const *fo, GOIOContext *io_context,
+G_MODULE_EXPORT void     lotus_file_open (GOFileOpener const *fo, GOIOContext *io_context,
                           WorkbookView *wb_view, GsfInput *input);
 
 
@@ -40,7 +42,8 @@
 		h = gsf_input_read (input, 6, NULL);
 
 	if (h == NULL ||
-	    GSF_LE_GET_GUINT16 (h + 0) != LOTUS_BOF)
+	    (GSF_LE_GET_GUINT16 (h + 0) != LOTUS_BOF &&
+		GSF_LE_GET_GUINT16 (h + 0) != WORKS_BOF))
 		return FALSE;
 
 	len = GSF_LE_GET_GUINT16 (h + 2);
Index: E:/TEMP/lotus_/lotus.c
===================================================================
--- E:/TEMP/lotus_/lotus.c	(revision 1)
+++ E:/TEMP/lotus_/lotus.c	(revision 4)
@@ -14,6 +14,8 @@
 #include "lotus-types.h"
 #include "lotus-formula.h"
 
+#include "works.h"
+
 #include <workbook.h>
 #include <cell.h>
 #include <expr.h>
@@ -29,7 +31,7 @@
 #include <gsf/gsf-msole-utils.h>
 #include <string.h>
 
-#define LOTUS_DEBUG 0
+#define LOTUS_DEBUG 1
 #undef DEBUG_RLDB
 #undef DEBUG_STYLE
 #undef DEBUG_FORMAT
@@ -2376,7 +2378,9 @@
 	record_t r;
 	r.input = state->input;
 
-	if (record_next (&r) && r.type == LOTUS_BOF) {
+	if (!record_next (&r)) return FALSE;
+
+	if (r.type == LOTUS_BOF) {
 		state->version = GSF_LE_GET_GUINT16 (r.data);
 #if LOTUS_DEBUG > 0
 		g_print ("Version=%x\n", state->version);
@@ -2396,6 +2400,13 @@
 		case LOTUS_VERSION_123SS98:
 			return lotus_read_new (state, &r);
 		}
+	} else if (r.type == WORKS_BOF) {
+		state->version = GSF_LE_GET_GUINT16 (r.data);
+#if LOTUS_DEBUG > 0
+		g_print ("Version=%x\n", state->version);
+#endif
+		if (state->version == WORKS_VERSION_3)
+			return lotus_read_works (state, &r);
 	}
 
 	return FALSE;
@@ -2411,3 +2422,434 @@
 {
 	gsf_iconv_close (lmbcs_12_iconv);
 }
+
+typedef struct {
+	const char *typeface;
+	int variant;
+	int size;
+	int codepage;
+} WksFontEntry;
+
+static WksFontEntry *wks_font_new()
+{
+	WksFontEntry *n = g_malloc( sizeof(WksFontEntry) );
+	n->codepage=n->size=n->variant=0;
+	n->typeface=NULL;
+	return n;
+}
+
+static void wks_font_dtor(WksFontEntry *s)
+{
+	if (s->typeface) g_free(s->typeface);
+	g_free(s);
+}
+
+void cell_set_fmt (LotusState *state, GnmCell *cell, int fmt)
+{
+	GnmRange range;
+	GnmStyle *style;
+
+	range.start = cell->pos;
+	range.end = range.start;
+	
+	style = g_hash_table_lookup(state->style_pool, 
+		GUINT_TO_POINTER ((guint)fmt));
+
+	if (style) {
+		gnm_style_ref(style);
+		sheet_apply_style(state->sheet,&range,style);
+	}
+}
+
+static const guint8 works_color_table[16][3]={
+	{0,0,0}, /* dummy */
+	{0,0,0},
+	{0,0,255},
+	{0,255,255},
+	{0,255,0},
+	{255,0,255},
+	{255,0,0},
+	{255,255,0},
+	{128,128,128},
+	{255,255,255},
+	{0,0,128},
+	{0,128,128},
+	{0,128,0},
+	{128,0,128},
+	{128,0,0},
+	{192,192,192}
+};
+
+static GnmColor *
+works_color (guint i)
+{
+	if (i == 0) return style_color_auto_font();
+	if (i < G_N_ELEMENTS (works_color_table))
+		return style_color_new_i8 (works_color_table[i][0],
+					   works_color_table[i][1],
+					   works_color_table[i][2]);
+	return NULL;
+}
+
+static const gchar* works_data_fmts[]=
+{
+	"dd.mm.yyyy",
+	"d mmmm yyyy",
+	"dd.yyyy",
+	"mmmm yyyy",
+	"dd.mm",
+	"d mmmm",
+	"dd-mm-yy", /* looks unused */
+	"mmmm"
+};
+
+static const gchar* works_time_fmts[]=
+{
+	"h:mm AM/PM",
+	"h:mm:ss AM/PM",
+	"h:mm",
+	"h:mm:ss"
+};
+
+static const gchar* works_frac_fmts[]=
+{
+	"# ??/??",
+	"# ?/4",
+	"#", /* unused ? */
+	"# ?/8",
+	"# ?/10",
+	"# ?/16",
+	"# ?/32",
+	"# ?/100"
+};
+
+char *works_format_string (guint8 arg)
+{
+	static char delme[40];
+	int type, prec;
+	GString *str;
+
+	type = arg & 0xf;
+	prec = (arg >> 5) & 7;
+
+	str = g_string_new(NULL);
+
+	switch (type) {
+		case 0: /* fixed */
+		case 2: /* currency */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			break;
+		case 1: /* exp */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			g_string_append(str,"E+00");
+			break;
+		case 3: /* percent */
+			g_string_append(str,"0");
+			append_precision(str,prec);
+			g_string_append(str,"%");
+			break;
+		case 4: /* thousands */
+			g_string_append(str,"# ##0");
+			append_precision(str,prec);
+			break;
+		case 5:
+
+			switch (prec) {
+				case 0: /* general */
+				case 1: /* boolean */
+					break;
+				case 2:
+				case 3:
+				case 4:
+				case 5:
+					g_string_append(str,works_time_fmts[prec-2]);
+					break;
+			};
+			break;
+		case 6:
+			g_string_append(str,works_data_fmts[prec]);
+			break;
+		case 10: /* fixed */
+			g_string_append_len(str,"000000000",prec+1);
+			break;
+		case 11: /* fraction (no ...) */
+		case 12: /* fraction */
+			if (type == 11) {
+				if (prec == 0) {
+					g_string_append(str,"# ?/2");
+					break;
+				} else if (prec == 1) {
+					g_string_append(str,"# ?/3");
+					break;
+				}
+			}
+			g_string_append(str,works_frac_fmts[prec]);
+			break;
+		case 13: /* currency + red */
+		case 14: /* thousands + red */
+			g_string_append(str,"# ##0");
+			append_precision(str,prec);
+			g_string_append(str,";[Red]-# ##0"); /* l10n??? */
+			append_precision(str,prec);
+			break;
+	}
+	return g_string_free(str, FALSE);
+}
+
+static gboolean
+lotus_read_works (LotusState *state, record_t *r)
+{
+	gboolean result = TRUE;
+	int sheetidx = 0;
+	int styleidx = 0;
+	int fontidx  = 0;
+	GnmCell    *cell;
+	GHashTable *fonts = NULL;
+	WksFontEntry *font;
+
+	state->style_pool = g_hash_table_new_full
+	(g_direct_hash,
+	 g_direct_equal,
+	 NULL,
+	 (GDestroyNotify)gnm_style_unref);
+
+	fonts = g_hash_table_new_full
+	(g_direct_hash,
+	 g_direct_equal,
+	 NULL,
+	 (GDestroyNotify)gnm_style_unref);
+
+	state->lmbcs_group = 1;
+
+	do {
+		switch (r->type) {
+		case WORKS_BOF:
+			state->sheet = attach_sheet (state->wb, sheetidx++);
+			break;
+
+		case LOTUS_EOF:
+			state->sheet = NULL;
+			break;
+
+		case WORKS_SMALL_FLOAT: CHECK_RECORD_SIZE (>= 6) {
+			int row = GSF_LE_GET_GUINT16 (r->data + 2);
+			int col = r->data[0];
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			guint32 raw = GSF_LE_GET_GUINT32 (r->data + 6);
+			char flag = raw & 1;
+			float x;
+			GnmValue *v;
+
+			raw = (raw&0xfc000000)|((raw&0x3fffffe)<<3);
+			x = *(float*) &raw;
+			v = lotus_value (flag ? x/100.0 : x);
+			(void)insert_value (state, state->sheet, col, row, v);
+			break;
+		}
+		/*case LOTUS_INTEGER: CHECK_RECORD_SIZE (>= 7) {
+			GnmValue *v = value_new_int (GSF_LE_GET_GINT16 (r->data + 5));
+			guint8 fmt = GSF_LE_GET_GUINT8 (r->data);
+			int i = GSF_LE_GET_GUINT16 (r->data + 1);
+			int j = GSF_LE_GET_GUINT16 (r->data + 3);
+
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_format_from_lotus_format (cell, fmt);
+			break;
+		}*/
+		case LOTUS_NUMBER: CHECK_RECORD_SIZE (>= 14) {
+			GnmValue *v = lotus_value (gsf_le_get_double (r->data + 6));
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_LABEL: CHECK_RECORD_SIZE (>= 8) {
+			GnmValue *v = lotus_get_strval (r, 6, state->lmbcs_group);
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_BLANK: CHECK_RECORD_SIZE (>= 6) {
+			GnmValue *v = value_new_empty();
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int i = GSF_LE_GET_GUINT16 (r->data + 0);
+			int j = GSF_LE_GET_GUINT16 (r->data + 2);
+			cell = insert_value (state, state->sheet, i, j, v);
+			if (cell)
+				cell_set_fmt (state, cell, fmt);
+			break;
+		}
+		case LOTUS_FORMULA: CHECK_RECORD_SIZE (>= 16) {
+			int fmt = GSF_LE_GET_GUINT16 (r->data + 4);
+			int col = GSF_LE_GET_GUINT16 (r->data + 0);
+			int row = GSF_LE_GET_GUINT16 (r->data + 2);
+			int len = GSF_LE_GET_GINT16 (r->data + 14);
+			GnmExprTop const *texpr;
+			GnmParsePos pp;
+			GnmValue *v = NULL;
+
+			if (r->len < (15 + len))
+				break;
+
+			pp.eval.col = col;
+			pp.eval.row = row;
+			pp.sheet = state->sheet;
+			pp.wb = pp.sheet->workbook;
+			texpr = lotus_parse_formula (state, &pp,
+						     r->data + 16, len);
+
+			if (0x7ff0 == (GSF_LE_GET_GUINT16 (r->data + 12) & 0x7ff8)) {
+				if (LOTUS_STRING == record_peek_next (r)) {
+					record_next (r);
+					v = lotus_get_strval (r, 6, state->lmbcs_group);
+				} else
+					v = value_new_error_VALUE (NULL);
+			} else
+				v = lotus_value (gsf_le_get_double (r->data + 6));
+			cell = lotus_cell_fetch (state, state->sheet, col, row);
+			if (cell) {
+				gnm_cell_set_expr_and_value (cell, texpr, v, TRUE);
+				cell_set_fmt (state, cell, fmt);
+			} else
+				value_release (v);
+			gnm_expr_top_unref (texpr);
+			break;
+		}
+
+		case WORKS_FONT: CHECK_RECORD_SIZE (>= 38) {
+			WksFontEntry *font = wks_font_new();
+			int fid = fontidx++;
+			int l;
+			font->variant = GSF_LE_GET_GUINT16(r->data + 0);
+			l=strlen(r->data + 2);
+			if (l > 34) l = 34;
+			font->typeface = g_malloc(l + 1);
+			/* verify UTF-8? */
+			memcpy(font->typeface, r->data + 2, l);
+			((char*)font->typeface)[l] = 0;
+			font->size = r->data[36];
+			g_hash_table_insert (fonts,
+			     GUINT_TO_POINTER ((guint)fid),
+			     font);
+			break;
+		}
+
+		case WORKS_STYLE: CHECK_RECORD_SIZE (>= 10) {
+			GnmStyle *style;
+			GnmColor *color;
+			WksFontEntry *font;
+			char *fmt;
+			int pat;
+			int tmp;
+
+			guint16 styleid = styleidx++;
+			guint8 fontid = GSF_LE_GET_GUINT8 (r->data + 4);
+			/*guint16 fontsize = GSF_LE_GET_GUINT16 (r->data + 10);
+			guint16 textbg = GSF_LE_GET_GUINT16 (r->data + 14);*/
+			guint16 facebits;
+			guint8 align = GSF_LE_GET_GUINT8 (r->data + 1);
+			/*guint16 angle = GSF_LE_GET_GUINT16 (r->data + 22);
+			guint16 intfg = GSF_LE_GET_GUINT16 (r->data + 24);
+			guint16 intbg = GSF_LE_GET_GUINT16 (r->data + 26);
+			guint8 intpat = GSF_LE_GET_GUINT8 (r->data + 28);*/
+
+			style = gnm_style_new ();
+
+			font = g_hash_table_lookup(fonts,GUINT_TO_POINTER ((guint)fontid));
+
+			if (font) {
+				facebits = font->variant;
+#define FACEBIT(bitval,attr)							\
+		if (facebits & bitval) 						\
+			g_print ("Bit value %d set.\n", bitval);		\
+		gnm_style_set_font_ ## attr (style, (facebits & bitval) != 0);
+				FACEBIT (0x0001,bold);
+				FACEBIT (0x0002,italic);
+				FACEBIT (0x0004,uline);
+				FACEBIT (0x0008,strike);
+#undef FACEBIT
+				if (font->size != 0)
+					gnm_style_set_font_size (style, font->size / 2.0);
+
+				if (font->variant & 0xF0) {
+					color = works_color((font->variant >> 4) & 0xF);
+					if (color)
+						gnm_style_set_font_color (style, color);
+				}
+
+				if (font->typeface)
+					gnm_style_set_font_name(style, font->typeface);
+			}
+
+			tmp = (align >> 2) & 7;
+			switch (tmp) {
+				case 1:
+					tmp = HALIGN_LEFT;
+					break;
+				case 2:
+					tmp = HALIGN_CENTER;
+					break;
+				case 3:
+					tmp = HALIGN_RIGHT;
+					break;
+				case 4:
+					tmp = HALIGN_FILL;
+					break;
+				default:
+					tmp = HALIGN_GENERAL;
+			}
+			gnm_style_set_align_h(style, tmp);
+
+			tmp = (align >> 6) & 3;
+			switch (tmp) {
+				case 0:
+					tmp = VALIGN_BOTTOM;
+					break;
+				case 1:
+					tmp = VALIGN_CENTER;
+					break;
+				case 2:
+					tmp = VALIGN_TOP;
+					break;
+			}
+			gnm_style_set_align_v(style, tmp);
+
+			if (align & 0x20) {
+				gnm_style_set_wrap_text(style, TRUE);
+			}
+
+			fmt = works_format_string(r->data[0]);
+			if (fmt) {
+				if (*fmt) gnm_style_set_format_text(style, fmt);
+				g_free(fmt);
+			}
+
+#ifdef DEBUG_STYLE
+			g_print ("Defining style 0x%04x:\n", styleid);
+			gnm_style_dump (style);
+#endif
+			g_hash_table_insert (state->style_pool,
+					     GUINT_TO_POINTER ((guint)styleid),
+					     style);
+		}
+
+		default:
+			break;
+		}
+	} while (record_next (r));
+
+	if (fonts) g_hash_table_destroy(fonts);
+
+	return result;
+}
\ No newline at end of file
Index: E:/TEMP/lotus_/lotus-formula.c
===================================================================
--- E:/TEMP/lotus_/lotus-formula.c	(revision 1)
+++ E:/TEMP/lotus_/lotus-formula.c	(revision 4)
@@ -560,6 +560,8 @@
 			i++;
 			break;
 		case 0x17: HANDLE_UNARY (GNM_EXPR_OP_UNARY_PLUS);
+		/* Works */
+		case 0x18: HANDLE_BINARY (GNM_EXPR_OP_CAT);
 
 		default:
 			i += make_function (&stack, data + i, orig);
Index: E:/TEMP/lotus_/works.h
===================================================================
--- E:/TEMP/lotus_/works.h	(revision 0)
+++ E:/TEMP/lotus_/works.h	(revision 4)
@@ -0,0 +1,11 @@
+#ifndef GNUMERIC_PLUGIN_LOTUS_123_WORKS_H
+#define GNUMERIC_PLUGIN_LOTUS_123_WORKS_H
+
+#define WORKS_VERSION_3 0x0404
+
+#define WORKS_BOF 0xff
+#define WORKS_FONT  0x5456
+#define WORKS_STYLE 0x545a
+#define WORKS_SMALL_FLOAT 0x545b
+
+#endif
