/** * ms-excel.c: MS Excel support for Gnumeric * * Author: * Michael Meeks (michael@imaginator.com) **/ #include #include #include #include #include #include #include #include #include #include #include #include "gnumeric.h" #include "gnumeric-util.h" #include "gnome-xml/tree.h" #include "gnome-xml/parser.h" #include "gnumeric-sheet.h" #include "format.h" #include "color.h" #include "sheet-object.h" #include "style.h" #include "ms-ole.h" #include "ms-biff.h" #include "ms-formula.h" #include "ms-excel.h" #include "ms-excel-biff.h" /* Forward references */ static MS_EXCEL_SHEET *ms_excel_sheet_new (MS_EXCEL_WORKBOOK * wb, char *name) ; static void ms_excel_workbook_attach (MS_EXCEL_WORKBOOK * wb, MS_EXCEL_SHEET * ans) ; /** * Generic 16 bit int index pointer functions. **/ static guint biff_guint16_hash (const guint16 *d) { return *d*2 ; } static guint biff_guint32_hash (const guint32 *d) { return *d*2 ; } static gint biff_guint16_equal (const guint16 *a, const guint16 *b) { if (*a==*b) return 1 ; return 0 ; } static gint biff_guint32_equal (const guint32 *a, const guint32 *b) { if (*a==*b) return 1 ; return 0 ; } /** * This function takes a length argument as Biff V7 has a byte length * ( seemingly ). * FIXME: see S59D47.HTM for full description **/ char * biff_get_text (BYTE * ptr, int length) { int lp ; char *ans; BYTE header ; gboolean high_byte ; gboolean ext_str ; gboolean rich_str ; if (!length) return 0 ; ans = (char *) g_malloc (sizeof (char) * length + 2); header = BIFF_GETBYTE(ptr) ; /* I assume that this header is backwards compatible with raw ASCII */ if (((header & 0xf0) == 0) && ((header & 0x02) == 0)) /* Its a proper Unicode header grbit byte */ { high_byte = (header & 0x1) != 0 ; ext_str = (header & 0x4) != 0 ; rich_str = (header & 0x8) != 0 ; ptr++ ; } else /* Some assumptions: FIXME ? */ { high_byte = 0 ; ext_str = 0 ; rich_str = 0 ; } /* A few friendly warnings */ if (high_byte) printf ("FIXME: unicode support unimplemented: truncating\n") ; if (rich_str) /* The data for this appears after the string */ { guint16 formatting_runs = BIFF_GETWORD(ptr) ; ptr+= 2 ; printf ("FIXME: rich string support unimplemented: discarding %d runs\n", formatting_runs) ; } if (ext_str) /* NB this data always comes after the rich_str data */ { guint32 len_ext_rst = BIFF_GETLONG(ptr) ; printf ("FIXME: extended string support unimplemented: ignoring %d bytes\n", len_ext_rst) ; ptr+= 4 ; } for (lp = 0; lp < length; lp++) { ans [lp] = (char) *ptr ; ptr += high_byte ? 2 : 1; } ans[lp] = 0; return ans; } static char * biff_get_global_string(MS_EXCEL_SHEET *sheet, int number) { MS_EXCEL_WORKBOOK *wb = sheet->wb; if (number >= wb->global_string_max) return "Too Weird"; return wb->global_strings[number] ; } char * biff_get_error_text (guint8 err) { char *buf ; switch (err) { case 0: buf = "#NULL!" ; break ; case 7: buf = "#DIV/0!" ; break ; case 15: buf = "#VALUE!" ; break ; case 23: buf = "#REF!" ; break ; case 29: buf = "#NAME?" ; break ; case 36: buf = "#NUM!" ; break ; case 42: buf = "#N/A" ; break ; default: buf = "#UNKNOWN!"; break ; } return buf ; } /** * See S59D5D.HTM **/ static BIFF_BOF_DATA * ms_biff_bof_data_new (BIFF_QUERY * q) { BIFF_BOF_DATA *ans = g_new (BIFF_BOF_DATA, 1); if ((q->opcode & 0xff) == BIFF_BOF){ assert (q->length >= 4); /* * Determine type from boff */ switch (q->opcode >> 8){ case 0: ans->version = eBiffV2; break; case 2: ans->version = eBiffV3; break; case 4: ans->version = eBiffV4; break; case 8: /* * More complicated */ { printf ("Complicated BIFF version %d\n", BIFF_GETWORD (q->data)); dump (q->data, q->length); switch (BIFF_GETWORD (q->data)) { case 0x0600: ans->version = eBiffV8; break; case 0x500: ans->version = eBiffV7; /* * OR ebiff7 : FIXME ? ! */ break; default: printf ("Unknown BIFF sub-number in BOF %x\n", q->opcode); ans->version = eBiffVUnknown; } } break; default: printf ("Unknown BIFF number in BOF %x\n", q->opcode); ans->version = eBiffVUnknown; printf ("Biff version %d\n", ans->version); } switch (BIFF_GETWORD (q->data + 2)){ case 0x0005: ans->type = eBiffTWorkbook; break; case 0x0006: ans->type = eBiffTVBModule; break; case 0x0010: ans->type = eBiffTWorksheet; break; case 0x0020: ans->type = eBiffTChart; break; case 0x0040: ans->type = eBiffTMacrosheet; break; case 0x0100: ans->type = eBiffTWorkspace; break; default: ans->type = eBiffTUnknown; printf ("Unknown BIFF type in BOF %x\n", BIFF_GETWORD (q->data + 2)); break; } /* * Now store in the directory array: */ printf ("BOF %x, %d == %d, %d\n", q->opcode, q->length, ans->version, ans->type); } else { printf ("Not a BOF !\n"); ans->version = eBiffVUnknown; ans->type = eBiffTUnknown; } return ans; } static void ms_biff_bof_data_destroy (BIFF_BOF_DATA * data) { g_free (data); } /** * See S59D61.HTM **/ static void biff_boundsheet_data_new (MS_EXCEL_WORKBOOK *wb, BIFF_QUERY * q, eBiff_version ver) { MS_EXCEL_SHEET *sheet ; BIFF_BOUNDSHEET_DATA *ans = g_new (BIFF_BOUNDSHEET_DATA, 1) ; if (ver != eBiffV5 && /* * Testing seems to indicate that Biff5 is compatibile with Biff7 here. */ ver != eBiffV7 && ver != eBiffV8){ printf ("Unknown BIFF Boundsheet spec. Assuming same as Biff7 FIXME\n"); ver = eBiffV7; } ans->streamStartPos = BIFF_GETLONG (q->data); switch (BIFF_GETBYTE (q->data + 4)){ case 00: ans->type = eBiffTWorksheet; break; case 01: ans->type = eBiffTMacrosheet; break; case 02: ans->type = eBiffTChart; break; case 06: ans->type = eBiffTVBModule; break; default: printf ("Unknown sheet type : %d\n", BIFF_GETBYTE (q->data + 4)); ans->type = eBiffTUnknown; break; } switch ((BIFF_GETBYTE (q->data + 5)) & 0x3){ case 00: ans->hidden = eBiffHVisible; break; case 01: ans->hidden = eBiffHHidden; break; case 02: ans->hidden = eBiffHVeryHidden; break; default: printf ("Unknown sheet hiddenness %d\n", (BIFF_GETBYTE (q->data + 4)) & 0x3); ans->hidden = eBiffHVisible; break; } if (ver == eBiffV8) { int strlen = BIFF_GETWORD (q->data + 6); ans->name = biff_get_text (q->data + 8, strlen); } else { int strlen = BIFF_GETBYTE (q->data + 6); ans->name = biff_get_text (q->data + 7, strlen); } /* * printf ("Blocksheet : '%s', %d:%d offset %lx\n", ans->name, ans->type, ans->hidden, ans->streamStartPos); */ ans->index = (guint16)g_hash_table_size (wb->boundsheet_data_by_index) ; g_hash_table_insert (wb->boundsheet_data_by_index, &ans->index, ans) ; g_hash_table_insert (wb->boundsheet_data_by_stream, &ans->streamStartPos, ans) ; g_assert (ans->streamStartPos == BIFF_GETLONG (q->data)) ; sheet = ms_excel_sheet_new (wb, ans->name); ms_excel_workbook_attach (wb, sheet); } static gboolean biff_boundsheet_data_destroy (gpointer key, BIFF_BOUNDSHEET_DATA *d, gpointer userdata) { g_free (d->name) ; g_free (d) ; return 1 ; } /** * Ug! FIXME **/ static char * biff_nasty_font_check_function (char *name1, char *name2) { if (gdk_font_load(name1)) { if (name2) g_free(name2) ; return name1 ; } else { if (name1) g_free(name1) ; return name2 ; } } static StyleFont* biff_font_data_get_style_font (BIFF_FONT_DATA *fd) { char font_size[16]; /* I know it may seem excessive. Time will say. */ int i; char *fname1, *fname2 ; StyleFont *ans ; /* * FIXME: instead of just copying the windows font into the cell, we * should implement a font name mapping mechanism. * In our first attempt to make it work, let's try to guess the * X font name from the windows name, by letting the first word * of the name be inserted in 0'th position of the X font name. */ for (i = 0; fd->fontname[i] != '\0' && fd->fontname[i] != ' '; ++i) fd->fontname[i] = tolower (fd->fontname[i]); fd->fontname[i] = '\x0'; fname1 = g_strdup (gnumeric_default_font->font_name) ; fname2 = font_change_component (gnumeric_default_font->font_name, 1, fd->fontname) ; fname1 = biff_nasty_font_check_function (fname2, fname1) ; /* printf ("FoNt [-]: %s\n", fname1) ; */ if (fd->italic) { fname2 = font_get_italic_name (fname1); /* printf ("FoNt [i]: %s\n", fname2) ; */ } else fname2 = g_strdup (fname1) ; fname1 = biff_nasty_font_check_function (fname2, fname1) ; if (fd->boldness >= 0x2bc) { fname2 = font_get_bold_name (fname1) ; /* printf ("FoNt [b]: %s\n", fname1) ; */ } else fname2 = g_strdup (fname1) ; fname1 = biff_nasty_font_check_function (fname2, fname1) ; /* What about underlining? */ g_assert (snprintf (font_size, 16, "%d", fd->height / 2) != -1); fname2 = font_change_component (fname1, 7, font_size) ; fname1 = biff_nasty_font_check_function (fname2, fname1) ; ans = style_font_new (fname1, 1) ; g_free (fname1) ; if (fd->italic) ans->hint_is_italic = 1 ; if (fd->boldness >= 0x2bc) ans->hint_is_bold = 1 ; return ans ; } /** * NB. 'fount' is the correct, and original _English_ **/ static void biff_font_data_new (MS_EXCEL_WORKBOOK *wb, BIFF_QUERY *q) { BIFF_FONT_DATA *fd = g_new (BIFF_FONT_DATA, 1); WORD data; fd->height = BIFF_GETWORD (q->data + 0); data = BIFF_GETWORD (q->data + 2); fd->italic = (data & 0x2) == 0x2; fd->struck_out = (data & 0x8) == 0x8; fd->color_idx = BIFF_GETWORD (q->data + 4); fd->boldness = BIFF_GETWORD (q->data + 6); data = BIFF_GETWORD (q->data + 8); switch (data){ case 0: fd->script = eBiffFSNone; break; case 1: fd->script = eBiffFSSuper; break; case 2: fd->script = eBiffFSSub; break; default: printf ("Unknown script %d\n", data); break; } data = BIFF_GETWORD (q->data + 10); switch (data){ case 0: fd->underline = eBiffFUNone; break; case 1: fd->underline = eBiffFUSingle; break; case 2: fd->underline = eBiffFUDouble; break; case 0x21: fd->underline = eBiffFUSingleAcc; break; case 0x22: fd->underline = eBiffFUDoubleAcc; break; } fd->fontname = biff_get_text (q->data + 15, BIFF_GETBYTE (q->data + 14)); printf ("Insert font '%s' size %d pts\n", fd->fontname, fd->height * 20); fd->style_font = 0 ; fd->index = g_hash_table_size (wb->font_data) ; if (fd->index >= 4) /* Wierd: for backwards compatibility */ fd->index++ ; g_hash_table_insert (wb->font_data, &fd->index, fd) ; } static gboolean biff_font_data_destroy (gpointer key, BIFF_FONT_DATA *fd, gpointer userdata) { g_free (fd->fontname) ; g_free (fd) ; if (fd->style_font) style_font_unref (fd->style_font) ; return 1 ; } static StyleFormat * biff_format_data_lookup (MS_EXCEL_WORKBOOK *wb, guint16 idx) { char *low_formats[] = { "", "0", "0.00", "#,##0", "#,##0.00", "($#,##0_);($#,##0)", "($#,##0_);[Red]($#,##0)", "($#,##0.00_);($#,##0.00)", "($#,##0.00_);[Red]($#,##0.00)", "0%", "0.00%", "0.00E+00", "#", "#", "m/d/yy", "d-mmm-yy", "d-mmm", "mmm-yy", "h:mm", "h:mm:ss", "h:mm", "h:mm:ss", "m/d/yy" } ; char *high_formats[] = { "(#,##0_);(#,##0)", "(#,##0_);[Red](#,##0)", "(#,##0.00_);(#,##0.00)", "(#,##0.00_);[Red](#,##0.00)", "_(*", "_($*", "_(*", "_($*", "mm:ss", "[h]:mm:ss", "mm:ss.0", "##0.0E+0", "@" } ; char *ans ; if (idx <= 0x16) ans = low_formats[idx] ; else if (idx < 0x25) { printf ("Foreign undocumented format\n") ; ans = 0 ; } else if (idx <= 0x31) ans = high_formats[idx-0x25] ; else { BIFF_FORMAT_DATA *d = g_hash_table_lookup (wb->format_data, &idx) ; if (!d) { printf ("Unknown format: 0x%x\n", idx) ; ans = 0 ; } else ans = d->name ; } if (ans) return style_format_new (ans) ; else return NULL ; } static gboolean biff_format_data_destroy (gpointer key, BIFF_FORMAT_DATA *d, gpointer userdata) { g_free (d->name) ; g_free (d) ; return 1 ; } static MS_EXCEL_PALETTE * ms_excel_palette_new (BIFF_QUERY * q) { int lp, len; MS_EXCEL_PALETTE *pal; pal = (MS_EXCEL_PALETTE *) g_malloc (sizeof (MS_EXCEL_PALETTE)); len = BIFF_GETWORD (q->data); pal->length = len; pal->red = g_new (int, len) ; pal->green = g_new (int, len) ; pal->blue = g_new (int, len) ; pal->gnum_cols = g_new (StyleColor *, len) ; printf ("New palette with %d entries\n", len); for (lp = 0; lp < len; lp++){ LONG num = BIFF_GETLONG (q->data + 2 + lp * 4); pal->red[lp] = (num & 0x00ff0000) >> 16; pal->green[lp] = (num & 0x0000ff00) >> 8; pal->blue[lp] = (num & 0x000000ff) >> 0; printf ("Colour %d : (%d,%d,%d)\n", lp, pal->red[lp], pal->green[lp], pal->blue[lp]); pal->gnum_cols[lp] = 0 ; } return pal; } static StyleColor * ms_excel_palette_get (MS_EXCEL_PALETTE *pal, guint idx) { if (idx < pal->length) { if (!pal->gnum_cols[idx]) pal->gnum_cols[idx] = style_color_new (pal->red[idx], pal->green[idx], pal->blue[idx]) ; return pal->gnum_cols[idx] ; } else return 0 ; } static void ms_excel_palette_destroy (MS_EXCEL_PALETTE * pal) { guint16 lp ; g_free (pal->red); g_free (pal->green); g_free (pal->blue); for (lp=0;lplength;lp++) if (pal->gnum_cols[lp]) style_color_unref (pal->gnum_cols[lp]) ; g_free (pal); } typedef struct _BIFF_XF_DATA { guint16 index ; guint16 font_idx; guint16 format_idx; StyleFormat *style_format ; eBiff_hidden hidden; eBiff_locked locked; eBiff_xftype xftype; /* * -- Very important field... */ eBiff_format format; WORD parentstyle; StyleHAlignFlags halign; StyleVAlignFlags valign; eBiff_wrap wrap; BYTE rotation; eBiff_eastern eastern; guint8 border_color[4]; /* * Array [StyleSide] */ StyleBorderType border_type[4]; /* * Array [StyleSide] */ eBiff_border_orientation border_orientation; StyleBorderType border_linestyle; BYTE fill_pattern_idx; BYTE foregnd_col; BYTE backgnd_col; } BIFF_XF_DATA; static void ms_excel_set_cell_colors (MS_EXCEL_SHEET * sheet, Cell * cell, BIFF_XF_DATA * xf) { MS_EXCEL_PALETTE *p = sheet->wb->palette; int col; if (!p || !xf) return; col = xf->foregnd_col; if (col >= 0 && col < sheet->wb->palette->length){ /* * printf ("FG set to %d = (%d,%d,%d)\n", col, p->red[col], p->green[col], p->blue[col]); */ cell_set_foreground (cell, p->red[col], p->green[col], p->blue[col]); } else printf ("FG col out of range %d\n", col); col = xf->backgnd_col; if (col >= 0 && col < sheet->wb->palette->length){ /* * printf ("BG set to %d = (%d,%d,%d)\n", col, p->red[col], p->green[col], p->blue[col]); */ cell_set_background (cell, p->red[col], p->green[col], p->blue[col]); } else printf ("BG col out of range %d\n", col); } /** * Search for a font record from its index in the workbooks font table * NB. index 4 is omitted supposedly for backwards compatiblity **/ static void ms_excel_set_cell_font (MS_EXCEL_SHEET * sheet, Cell * cell, BIFF_XF_DATA * xf) { BIFF_FONT_DATA *fd = g_hash_table_lookup (sheet->wb->font_data, &xf->font_idx) ; if (!fd) { printf ("Unknown fount idx %d\n", xf->font_idx); return ; } g_assert (fd->index != 4); if (!fd->style_font) fd->style_font = biff_font_data_get_style_font (fd) ; if (fd->style_font) cell_set_font_from_style (cell, fd->style_font) ; else printf ("Duff StyleFont\n") ; } void ms_excel_set_cell_xf (MS_EXCEL_SHEET * sheet, Cell * cell, guint16 xfidx) { GList *ptr; int cnt; BIFF_XF_DATA *xf; if (xfidx == 0){ /* printf ("Normal cell formatting\n"); */ return; } if (xfidx == 15){ /* printf ("Default cell formatting\n"); */ return; } /* * if (!cell->text) Crash if formatting and no text... * cell_set_text_simple(cell, ""); * printf ("Looking for %d\n", xfidx); */ xf = g_hash_table_lookup (sheet->wb->XF_cell_records, &xfidx) ; if (!xf) { printf ("No XF record for %d out of %d found :-(\n", xfidx, g_hash_table_size (sheet->wb->XF_cell_records)); return; } if (xf->xftype != eBiffXCell) printf ("FIXME: Error looking up XF\n") ; /* * Well set it up then ! FIXME: hack ! */ cell_set_alignment (cell, xf->halign, xf->valign, ORIENT_HORIZ, 1); ms_excel_set_cell_colors (sheet, cell, xf); ms_excel_set_cell_font (sheet, cell, xf); if (sheet->wb->palette) { int lp; StyleColor *tmp[4]; for (lp=0;lp<4;lp++) tmp[lp] = ms_excel_palette_get (sheet->wb->palette, xf->border_color[lp]) ; cell_set_border (cell, xf->border_type, tmp); } if (xf->format_idx>0) { if (xf->style_format) cell_set_format_from_style (cell, xf->style_format) ; else { xf->style_format = biff_format_data_lookup (sheet->wb, xf->format_idx) ; if (xf->style_format) cell_set_format_from_style (cell, xf->style_format) ; } } } static StyleBorderType biff_xf_map_border (int b) { switch (b) { case 0: /* None */ return BORDER_NONE; case 1: /* Thin */ return BORDER_THIN; case 2: /* Medium */ return BORDER_MEDIUM; case 3: /* Dashed */ return BORDER_DASHED; case 4: /* Dotted */ return BORDER_DOTTED; case 5: /* Thick */ return BORDER_THICK; case 6: /* Double */ return BORDER_DOUBLE; case 7: /* Hair */ return BORDER_HAIR; case 8: /* Medium Dashed */ return BORDER_MEDIUM; case 9: /* Dash Dot */ return BORDER_THIN; case 10: /* Medium Dash Dot */ return BORDER_THIN; case 11: /* Dash Dot Dot */ return BORDER_HAIR; case 12: /* Medium Dash Dot Dot */ return BORDER_THIN; case 13: /* Slanted Dash Dot*/ return BORDER_HAIR; } printf ("Unknown border style %d\n", b); return BORDER_NONE; } /** * Parse the BIFF XF Data structure into a nice form, see S59E1E.HTM **/ static void biff_xf_data_new (MS_EXCEL_WORKBOOK *wb, BIFF_QUERY * q, eBiff_version ver) { BIFF_XF_DATA *xf = (BIFF_XF_DATA *) g_malloc (sizeof (BIFF_XF_DATA)); LONG data, subdata; xf->font_idx = BIFF_GETWORD (q->data); xf->format_idx = BIFF_GETWORD (q->data + 2); xf->style_format = 0 ; data = BIFF_GETWORD (q->data + 4); xf->locked = (data & 0x0001) ? eBiffLLocked : eBiffLUnlocked; xf->hidden = (data & 0x0002) ? eBiffHHidden : eBiffHVisible; xf->xftype = (data & 0x0004) ? eBiffXStyle : eBiffXCell; xf->format = (data & 0x0008) ? eBiffFLotus : eBiffFMS; xf->parentstyle = (data >> 4); data = BIFF_GETWORD (q->data + 6); subdata = data & 0x0007; switch (subdata){ case 0: xf->halign = HALIGN_GENERAL; break; case 1: xf->halign = HALIGN_LEFT; break; case 2: xf->halign = HALIGN_CENTER; break; case 3: xf->halign = HALIGN_RIGHT; break; case 4: xf->halign = HALIGN_FILL; break; case 5: xf->halign = HALIGN_JUSTIFY; break; case 6: xf->halign = HALIGN_JUSTIFY; /* * xf->halign = HALIGN_CENTRE_ACROSS_SELECTION; */ break; default: xf->halign = HALIGN_JUSTIFY; printf ("Unknown halign %d\n", subdata); break; } xf->wrap = (data & 0x0008) ? eBiffWWrap : eBiffWNoWrap; subdata = (data & 0x0070) >> 4; switch (subdata){ case 0: xf->valign = VALIGN_TOP; break; case 1: xf->valign = VALIGN_CENTER; break; case 2: xf->valign = VALIGN_BOTTOM; break; case 3: xf->valign = VALIGN_JUSTIFY; break; default: printf ("Unknown valign %d\n", subdata); break; } /* * FIXME: ignored bit 0x0080 */ if (ver == eBiffV8) xf->rotation = (data >> 8); else { subdata = (data & 0x0300) >> 8; switch (subdata){ case 0: xf->rotation = 0; break; case 1: xf->rotation = 255; /* * vertical letters no rotation */ break; case 2: xf->rotation = 90; /* * 90deg anti-clock */ break; case 3: xf->rotation = 180; /* * 90deg clock */ break; } } if (ver == eBiffV8){ /* * FIXME: Got bored and stop implementing everything, there is just too much ! */ data = BIFF_GETWORD (q->data + 8); subdata = (data & 0x00C0) >> 10; switch (subdata){ case 0: xf->eastern = eBiffEContext; break; case 1: xf->eastern = eBiffEleftToRight; break; case 2: xf->eastern = eBiffErightToLeft; break; default: printf ("Unknown location %d\n", subdata); break; } } if (ver == eBiffV8){ /* * Very different */ data = BIFF_GETWORD (q->data + 10); subdata = data; xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0xf); subdata = subdata >> 4; xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0xf); subdata = subdata >> 4; xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0xf); subdata = subdata >> 4; xf->border_type[STYLE_BOTTOM] = biff_xf_map_border (subdata & 0xf); subdata = subdata >> 4; data = BIFF_GETWORD (q->data + 12); subdata = data; xf->border_color[STYLE_LEFT] = (subdata & 0x7f); subdata = subdata >> 7; xf->border_color[STYLE_RIGHT] = (subdata & 0x7f); subdata = (data & 0xc000) >> 14; switch (subdata){ case 0: xf->border_orientation = eBiffBONone; break; case 1: xf->border_orientation = eBiffBODiagDown; break; case 2: xf->border_orientation = eBiffBODiagUp; break; case 3: xf->border_orientation = eBiffBODiagBoth; break; } data = BIFF_GETLONG (q->data + 14); subdata = data; xf->border_color[STYLE_TOP] = (subdata & 0x7f); subdata = subdata >> 7; xf->border_color[STYLE_BOTTOM] = (subdata & 0x7f); subdata = subdata >> 7; xf->border_linestyle = biff_xf_map_border ((data & 0x01e00000) >> 21); xf->fill_pattern_idx = biff_xf_map_border ((data & 0xfc000000) >> 26); data = BIFF_GETWORD (q->data + 18); xf->foregnd_col = (data & 0x007f); xf->backgnd_col = (data & 0x3f80) >> 7; } else { data = BIFF_GETWORD (q->data + 8); xf->foregnd_col = (data & 0x007f); xf->backgnd_col = (data & 0x1f80) >> 7; data = BIFF_GETWORD (q->data + 10); xf->fill_pattern_idx = data & 0x03f; /* * Luckily this maps nicely onto the new set. */ xf->border_type[STYLE_BOTTOM] = biff_xf_map_border ((data & 0x1c0) >> 6); xf->border_color[STYLE_BOTTOM] = (data & 0xfe00) >> 9; data = BIFF_GETWORD (q->data + 12); subdata = data; xf->border_type[STYLE_TOP] = biff_xf_map_border (subdata & 0x07); subdata = subdata >> 3; xf->border_type[STYLE_LEFT] = biff_xf_map_border (subdata & 0x07); subdata = subdata >> 3; xf->border_type[STYLE_RIGHT] = biff_xf_map_border (subdata & 0x07); subdata = subdata >> 3; xf->border_color[STYLE_TOP] = subdata; data = BIFF_GETWORD (q->data + 14); subdata = data; xf->border_color[STYLE_LEFT] = (subdata & 0x7f); subdata = subdata >> 7; xf->border_color[STYLE_RIGHT] = (subdata & 0x7f); } if (xf->xftype == eBiffXCell) { xf->index = 16 + 4 + g_hash_table_size (wb->XF_cell_records) ; /* printf ("Inserting into cell XF hash with : %d\n", xf->index) ; */ g_hash_table_insert (wb->XF_cell_records, &xf->index, xf) ; } else { xf->index = 16 + 4 + g_hash_table_size (wb->XF_style_records) ; /* printf ("Inserting into style XF hash with : %d\n", xf->index) ; */ g_hash_table_insert (wb->XF_style_records, &xf->index, xf) ; } } static gboolean biff_xf_data_destroy (gpointer key, BIFF_XF_DATA *xf, gpointer userdata) { if (xf->style_format) style_format_unref (xf->style_format) ; g_free (xf); return 1 ; } static MS_EXCEL_SHEET * ms_excel_sheet_new (MS_EXCEL_WORKBOOK * wb, char *name) { MS_EXCEL_SHEET *ans = (MS_EXCEL_SHEET *) g_malloc (sizeof (MS_EXCEL_SHEET)); ans->gnum_sheet = sheet_new (wb->gnum_wb, name); ans->blank = 1 ; ans->wb = wb; ans->array_formulae = 0; return ans; } static void ms_excel_sheet_set_version (MS_EXCEL_SHEET *sheet, eBiff_version ver) { sheet->ver = ver ; } void ms_excel_sheet_insert (MS_EXCEL_SHEET * sheet, int xfidx, int col, int row, char *text) { Cell *cell = sheet_cell_fetch (sheet->gnum_sheet, col, row); /* NB. cell_set_text _certainly_ strdups *text */ if (text) { sheet->blank = 0 ; cell_set_text (cell, text); } else cell_set_text (cell, "") ; ms_excel_set_cell_xf (sheet, cell, xfidx); } static void ms_excel_sheet_set_index (MS_EXCEL_SHEET *ans, int idx) { ans->index = idx ; } static void ms_excel_sheet_destroy (MS_EXCEL_SHEET * sheet) { GList *ptr = g_list_first (sheet->array_formulae); while (ptr){ g_free (ptr->data); ptr = ptr->next; } g_list_free (sheet->array_formulae); sheet_destroy (sheet->gnum_sheet); g_free (sheet); } static MS_EXCEL_WORKBOOK * ms_excel_workbook_new () { MS_EXCEL_WORKBOOK *ans = (MS_EXCEL_WORKBOOK *) g_malloc (sizeof (MS_EXCEL_WORKBOOK)); ans->extern_sheets = NULL ; ans->gnum_wb = NULL; /* Boundsheet data hashed twice */ ans->boundsheet_data_by_stream = g_hash_table_new ((GHashFunc)biff_guint32_hash, (GCompareFunc)biff_guint32_equal) ; ans->boundsheet_data_by_index = g_hash_table_new ((GHashFunc)biff_guint16_hash, (GCompareFunc)biff_guint16_equal) ; ans->font_data = g_hash_table_new ((GHashFunc)biff_guint16_hash, (GCompareFunc)biff_guint16_equal) ; ans->excel_sheets = NULL; ans->XF_style_records = g_hash_table_new ((GHashFunc)biff_guint16_hash, (GCompareFunc)biff_guint16_equal) ; ans->XF_cell_records = g_hash_table_new ((GHashFunc)biff_guint16_hash, (GCompareFunc)biff_guint16_equal) ; ans->format_data = g_hash_table_new ((GHashFunc)biff_guint16_hash, (GCompareFunc)biff_guint16_equal) ; ans->palette = NULL; ans->global_strings = NULL; ans->global_string_max = 0; return ans; } static void ms_excel_workbook_attach (MS_EXCEL_WORKBOOK * wb, MS_EXCEL_SHEET * ans) { int idx = 0 ; GList *list = wb->excel_sheets ; workbook_attach_sheet (wb->gnum_wb, ans->gnum_sheet); while (list) { idx++ ; list = list->next ; } ms_excel_sheet_set_index (ans, idx) ; wb->excel_sheets = g_list_append (wb->excel_sheets, ans) ; } static void ms_excel_workbook_detach (MS_EXCEL_WORKBOOK * wb, MS_EXCEL_SHEET * ans) { int idx = 0 ; GList *list = wb->excel_sheets ; if (ans->gnum_sheet) workbook_detach_sheet (wb->gnum_wb, ans->gnum_sheet); while (list) if (list->data == ans) { wb->excel_sheets = g_list_remove(wb->excel_sheets, list); return ; } else list = list->next ; } static MS_EXCEL_SHEET * ms_excel_workbook_get_sheet (MS_EXCEL_WORKBOOK *wb, int idx) { GList *list = wb->excel_sheets ; while (list) { MS_EXCEL_SHEET *sheet = list->data ; if (sheet->index == idx) return sheet ; list = list->next ; } return NULL ; } static void ms_excel_workbook_destroy (MS_EXCEL_WORKBOOK * wb) { g_hash_table_foreach_remove (wb->boundsheet_data_by_stream, (GHRFunc)biff_boundsheet_data_destroy, wb) ; g_hash_table_destroy (wb->boundsheet_data_by_index) ; g_hash_table_destroy (wb->boundsheet_data_by_stream) ; g_hash_table_foreach_remove (wb->XF_style_records, (GHRFunc)biff_xf_data_destroy, wb) ; g_hash_table_destroy (wb->XF_style_records) ; g_hash_table_foreach_remove (wb->XF_cell_records, (GHRFunc)biff_xf_data_destroy, wb) ; g_hash_table_destroy (wb->XF_cell_records) ; g_hash_table_foreach_remove (wb->font_data, (GHRFunc)biff_font_data_destroy, wb) ; g_hash_table_destroy (wb->font_data) ; g_hash_table_foreach_remove (wb->format_data, (GHRFunc)biff_format_data_destroy, wb) ; g_hash_table_destroy (wb->font_data) ; if (wb->palette) ms_excel_palette_destroy (wb->palette); if (wb->extern_sheets) g_free (wb->extern_sheets) ; g_free (wb); } /** * Unpacks a MS Excel RK structure, * This needs to return / insert sensibly to keep precision / accelerate **/ static double biff_get_rk (guint8 *ptr) { LONG number; LONG tmp[2]; double answer; enum eType { eIEEE = 0, eIEEEx10 = 1, eInt = 2, eIntx100 = 3 } type; number = BIFF_GETLONG (ptr); type = (number & 0x3); switch (type){ case eIEEE: tmp[0] = 0; tmp[1] = number & 0xfffffffc; answer = BIFF_GETDOUBLE (((BYTE *) tmp)); break; case eIEEEx10: tmp[0] = 0; tmp[1] = number & 0xfffffffc; answer = BIFF_GETDOUBLE (((BYTE *) tmp)); answer /= 100.0; break; case eInt: answer = (double) (number >> 2); break; case eIntx100: answer = ((double) (number >> 2)) / 100.0; break; default: printf ("You don't exist go away\n"); answer = 0; } return answer ; } /** * Parse the cell BIFF tag, and act on it as neccessary * NB. Microsoft Docs give offsets from start of biff record, subtract 4 their docs. **/ static void ms_excel_read_cell (BIFF_QUERY * q, MS_EXCEL_SHEET * sheet) { Cell *cell; switch (q->ls_op){ case BIFF_BLANK: /* * FIXME: Not a good way of doing blanks ? */ /* * printf ("Cell [%d, %d] XF = %x\n", EX_GETCOL(q), EX_GETROW(q), * EX_GETXF(q)); */ ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), 0); break; case BIFF_MULBLANK: /* * S59DA7.HTM is extremely unclear, this is an educated guess */ { if (q->opcode == BIFF_DV){ printf ("Unimplemented DV: data validation criteria, FIXME\n"); break; } else { int row, col, lastcol; int incr; BYTE *ptr; /* * dump (ptr, q->length); */ row = EX_GETROW (q); col = EX_GETCOL (q); ptr = (q->data + 4); lastcol = BIFF_GETWORD (q->data + q->length - 2); /* * guess */ /* printf ("Cells in row %d are blank starting at col %d until col %d (0x%x)\n", row, col, lastcol, lastcol); */ /* * if (lastcol col) ? 1 : -1; /* * g_assert (((lastcol-col)*incr+1)*2+6<=q->length); */ while (col != lastcol){ ms_excel_sheet_insert (sheet, BIFF_GETWORD (ptr), EX_GETCOL (q), EX_GETROW (q), 0); col += incr; ptr += 2; } } } break; case BIFF_HEADER: /* FIXME : S59D94 */ { char *str ; if (q->length) { printf ("Header '%s'\n", (str=biff_get_text (q->data+1, BIFF_GETBYTE(q->data)))) ; g_free(str) ; } break; } case BIFF_FOOTER: /* FIXME : S59D8D */ { char *str ; if (q->length) { printf ("Footer '%s'\n", (str=biff_get_text (q->data+1, BIFF_GETBYTE(q->data)))) ; g_free(str) ; } break; } case BIFF_RSTRING: { char *txt ; /* printf ("Cell [%d, %d] = ", EX_GETCOL(q), EX_GETROW(q)); dump (q->data, q->length); */ printf ("Rstring\n") ; ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), (txt = biff_get_text (q->data + 8, EX_GETSTRLEN (q)))); g_free (txt) ; break; } case BIFF_DBCELL: /* S59D6D.HTM */ /* Can be ignored on read side */ break ; case BIFF_NUMBER: { char buf[65]; double num = BIFF_GETDOUBLE (q->data + 6); /* * FIXME GETDOUBLE is not endian independant */ /* dump (q->data, q->length); snprintf (buf, 64, "NUM %f", num); */ snprintf (buf, 64, "%f", num); ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), buf); break; } case BIFF_RK: /* * FIXME: S59DDA.HTM - test IEEE stuff on other endian platforms */ { char buf[65]; /* printf ("RK number : 0x%x, length 0x%x\n", q->opcode, q->length); dump (q->data, q->length);*/ snprintf (buf, 64, "%f", biff_get_rk(q->data+6)); ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), buf); break; } case BIFF_MULRK: /* S59DA8.HTM */ { guint32 col, row, lastcol ; char buf[65] ; guint8 *ptr = q->data ; /* printf ("MULRK\n") ; dump (q->data, q->length) ; */ row = BIFF_GETWORD(q->data) ; col = BIFF_GETWORD(q->data+2) ; ptr+= 4 ; lastcol = BIFF_GETWORD(q->data+q->length-2) ; /* g_assert ((lastcol-firstcol)*6 == q->length-6 */ g_assert (lastcol>=col) ; while (col<=lastcol) { /* 2byte XF, 4 byte RK */ snprintf (buf, 64, "%f", biff_get_rk(ptr+2)) ; ms_excel_sheet_insert(sheet, BIFF_GETWORD(ptr), col, row, buf) ; col++ ; ptr+= 6 ; } break ; } case BIFF_LABEL: { char *label ; ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), (label = biff_get_text (q->data + 8, EX_GETSTRLEN (q)))); g_free (label) ; break; } case BIFF_ROW: /* * FIXME */ /* * printf ("Row %d formatting\n", EX_GETROW(q)); */ break; case BIFF_ARRAY: /* * FIXME: S59D57.HTM */ printf ("Array Formula\n") ; case BIFF_FORMULA: /* * FIXME: S59D8F.HTM */ /* * case BIFF_NAME FIXME: S59DA9.HTM */ ms_excel_parse_formula (sheet, q, EX_GETCOL (q), EX_GETROW (q)); break; case BIFF_LABELSST: { char *str; guint32 idx = BIFF_GETLONG (q->data + 6) ; if (!sheet->wb->global_strings || idx >= sheet->wb->global_string_max) printf ("string index 0x%x out of range\n", idx) ; else { str = sheet->wb->global_strings[idx] ; ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), str); } break; } default: switch (q->opcode) { case BIFF_NAME: /* FIXME: S59DA9.HTM */ { guint16 flags = BIFF_GETWORD(q->data) ; guint16 fn_grp_idx ; guint8 kb_shortcut = BIFF_GETBYTE(q->data+2); guint8 name_len = BIFF_GETBYTE(q->data+3) ; guint16 name_def_len = BIFF_GETWORD(q->data+4) ; guint8* name_def_data = q->data+14+name_def_len ; guint16 sheet_idx = BIFF_GETWORD(q->data+6) ; guint16 ixals = BIFF_GETWORD(q->data+8) ; /* dup */ guint8 menu_txt_len = BIFF_GETBYTE(q->data+10) ; guint8 descr_txt_len = BIFF_GETBYTE(q->data+11) ; guint8 help_txt_len = BIFF_GETBYTE(q->data+12) ; guint8 status_txt_len = BIFF_GETBYTE(q->data+13) ; char *name, *menu_txt, *descr_txt, *help_txt, *status_txt ; guint8 *ptr ; g_assert (ixals==sheet_idx) ; ptr = q->data + 14 ; name = biff_get_text (ptr, name_len) ; ptr+= name_len + name_def_len ; menu_txt = biff_get_text (ptr, menu_txt_len) ; ptr+= menu_txt_len ; descr_txt = biff_get_text (ptr, descr_txt_len) ; ptr+= descr_txt_len ; help_txt = biff_get_text (ptr, help_txt_len) ; ptr+= help_txt_len ; status_txt = biff_get_text (ptr, status_txt_len) ; printf ("Name record : '%s', '%s', '%s', '%s', '%s'\n", name, menu_txt, descr_txt, help_txt, status_txt) ; dump (name_def_data, name_def_len) ; /* Unpack flags */ fn_grp_idx = (flags&0xfc0)>>6 ; if ((flags&0x0001) != 0) printf (" Hidden") ; if ((flags&0x0002) != 0) printf (" Function") ; if ((flags&0x0004) != 0) printf (" VB-Proc") ; if ((flags&0x0008) != 0) printf (" Proc") ; if ((flags&0x0010) != 0) printf (" CalcExp") ; if ((flags&0x0020) != 0) printf (" BuiltIn") ; if ((flags&0x1000) != 0) printf (" BinData") ; printf ("\n") ; break ; } case BIFF_STRING: /* FIXME: S59DE9.HTM */ { char *txt ; printf ("This cell evaluated to '%s': so what ? data:\n", (txt = biff_get_text (q->data + 2, BIFF_GETWORD(q->data)))) ; if (txt) g_free (txt) ; break ; } case BIFF_BOOLERR: /* S59D5F.HTM */ { if (BIFF_GETBYTE(q->data + 7)) /* Error */ ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q) , EX_GETROW (q), biff_get_error_text (BIFF_GETBYTE(q->data + 6))) ; else /* Boolean */ { char *bl = "0" ; /* FALSE */ if (BIFF_GETBYTE(q->data + 6)) bl = "1" ; /* TRUE */ ms_excel_sheet_insert (sheet, EX_GETXF (q), EX_GETCOL (q), EX_GETROW (q), bl) ; } break; } default: printf ("Unrecognised opcode : 0x%x, length 0x%x\n", q->opcode, q->length); break; } } } static void ms_excel_read_sheet (MS_EXCEL_SHEET *sheet, BIFF_QUERY * q, MS_EXCEL_WORKBOOK * wb) { LONG blankSheetPos = q->streamPos + q->length + 4; printf ("----------------- Sheet -------------\n"); while (ms_biff_query_next (q)){ switch (q->ls_op){ case BIFF_EOF: if (q->streamPos == blankSheetPos || sheet->blank) { printf ("Blank sheet\n"); ms_excel_workbook_detach (sheet->wb, sheet) ; ms_excel_sheet_destroy (sheet) ; return; } ms_excel_fixup_array_formulae (sheet); return; break; default: ms_excel_read_cell (q, sheet); break; } } ms_excel_workbook_detach (sheet->wb, sheet) ; ms_excel_sheet_destroy (sheet) ; printf ("Error, hit end without EOF\n"); return; } char* biff_get_externsheet_name(MS_EXCEL_WORKBOOK *wb, guint16 idx, gboolean get_first) { BIFF_EXTERNSHEET_DATA *bed ; BIFF_BOUNDSHEET_DATA *bsd ; guint16 index ; g_assert ( idx < wb->num_extern_sheets) ; bed = &wb->extern_sheets[idx] ; index = get_first ? bed->first_tab : bed->last_tab ; bsd = g_hash_table_lookup (wb->boundsheet_data_by_index, &index) ; if (!bsd) return 0 ; return bsd->name ; } /** * Find a stream with the correct name **/ static MS_OLE_STREAM * find_workbook (MS_OLE * ptr) { /* * Find the right Stream ... John 4:13-14 */ MS_OLE_DIRECTORY *d = ms_ole_directory_new (ptr); /* * The thing to seek; first the kingdom of God, then this: */ while (ms_ole_directory_next (d)) { if (d->type == MS_OLE_PPS_STREAM) { int hit = 0; /* * printf ("Checking '%s'\n", d->name); */ hit |= (strncasecmp (d->name, "book", 4) == 0); hit |= (strncasecmp (d->name, "workbook", 8) == 0); if (hit) { MS_OLE_STREAM *stream ; printf ("Found Excel Stream : %s\n", d->name); stream = ms_ole_stream_open (d, 'r') ; ms_ole_directory_destroy (d) ; return stream ; } } } printf ("No Excel file found\n"); ms_ole_directory_destroy (d) ; return 0; } Workbook * ms_excelReadWorkbook (MS_OLE * file) { MS_EXCEL_WORKBOOK *wb = NULL; xmlNodePtr child; if (1){ /* ? */ MS_OLE_STREAM *stream; BIFF_QUERY *q; BIFF_BOF_DATA *ver = 0; int current_sheet = 0 ; /* * Tabulate frequencies for testing */ /* { int freq[256]; int lp; printf ("--------- BIFF Usage Chart ----------\n"); for (lp = 0; lp < 256; lp++) freq[lp] = 0; stream = find_workbook (file); q = ms_biff_query_new (stream); while (ms_biff_query_next (q)) freq[q->ls_op]++; for (lp = 0; lp < 256; lp++) if (freq[lp] > 0) printf ("Opcode 0x%x : %d\n", lp, freq[lp]); printf ("--------- End Usage Chart ----------\n"); ms_biff_query_destroy (q); ms_ole_stream_close (stream); } */ /* * Find that book file */ stream = find_workbook (file); q = ms_biff_query_new (stream); while (ms_biff_query_next (q)) { switch (q->ls_op) { case BIFF_BOF: { /* The first BOF seems to be OK, the rest lie ? */ eBiff_version vv = eBiffVUnknown; if (ver) { vv = ver->version; ms_biff_bof_data_destroy (ver); } ver = ms_biff_bof_data_new (q); if (vv != eBiffVUnknown) ver->version = vv; if (ver->type == eBiffTWorkbook){ wb = ms_excel_workbook_new (); wb->gnum_wb = workbook_new (); } else if (ver->type == eBiffTWorksheet) { BIFF_BOUNDSHEET_DATA *bsh ; bsh = g_hash_table_lookup (wb->boundsheet_data_by_stream, &q->streamPos) ; if (!bsh) printf ("Sheet offset in stream of %x not found in list\n", q->streamPos); else { MS_EXCEL_SHEET *sheet = ms_excel_workbook_get_sheet (wb, current_sheet) ; ms_excel_sheet_set_version (sheet, ver->version) ; ms_excel_read_sheet (sheet, q, wb) ; current_sheet++ ; } } else printf ("Unknown BOF\n"); } break; case BIFF_EOF: printf ("End of worksheet spec.\n"); break; case BIFF_BOUNDSHEET: biff_boundsheet_data_new (wb, q, ver->version); break; case BIFF_PALETTE: printf ("READ PALETTE\n"); wb->palette = ms_excel_palette_new (q); break; case BIFF_FONT: /* see S59D8C.HTM */ { BIFF_FONT_DATA *ptr; /* printf ("Read Font\n"); dump (q->data, q->length); */ biff_font_data_new (wb, q); } break; case BIFF_PRECISION: /* * FIXME: */ printf ("Opcode : 0x%x, length 0x%x\n", q->opcode, q->length); dump (q->data, q->length); break; case BIFF_XF_OLD: /* * FIXME: see S59E1E.HTM */ case BIFF_XF: biff_xf_data_new (wb, q, ver->version) ; break; case BIFF_SST: /* see S59DE7.HTM */ { int length, k ; char *temp ; wb->global_string_max = BIFF_GETLONG(q->data+4); wb->global_strings = g_new (char *, wb->global_string_max) ; temp = q->data + 8 ; for (k = 0; k < wb->global_string_max; k++) { length = BIFF_GETWORD (temp) ; wb->global_strings[k] = biff_get_text (temp+2, length) ; if (!wb->global_strings[k]) printf ("Blank string in table at : %d\n", k) ; temp+= length + 3 ; } break; } case BIFF_EXTERNSHEET: { if ( ver->version == eBiffV8 ) { guint16 numXTI = BIFF_GETWORD(q->data) ; guint16 cnt ; wb->num_extern_sheets = numXTI ; /* printf ("ExternSheet (%d entries)\n", numXTI) ; dump (q->data, q->length); */ wb->extern_sheets = g_new (BIFF_EXTERNSHEET_DATA, numXTI+1) ; for (cnt=0; cnt < numXTI; cnt++) { wb->extern_sheets[cnt].sup_idx = BIFF_GETWORD(q->data + 2 + cnt*6 + 0) ; wb->extern_sheets[cnt].first_tab = BIFF_GETWORD(q->data + 2 + cnt*6 + 2) ; wb->extern_sheets[cnt].last_tab = BIFF_GETWORD(q->data + 2 + cnt*6 + 4) ; /* printf ("SupBook : %d First sheet %d, Last sheet %d\n", BIFF_GETWORD(q->data + 2 + cnt*6 + 0), BIFF_GETWORD(q->data + 2 + cnt*6 + 2), BIFF_GETWORD(q->data + 2 + cnt*6 + 4)) ; */ } } else { printf ("ExternSheet : only BIFF8 supported so far...\n") ; } break ; } case BIFF_FORMAT: /* S59D8E.HTM */ { BIFF_FORMAT_DATA *d = g_new(BIFF_FORMAT_DATA,1) ; /* printf ("Format data 0x%x %d\n", q->ms_op, ver->version) ; dump (q->data, q->length) ;*/ if (ver->version == eBiffV7) /* Totaly guessed */ { d->idx = BIFF_GETWORD(q->data) ; d->name = biff_get_text(q->data+3, BIFF_GETBYTE(q->data+2)) ; } else if (ver->version == eBiffV8) { d->idx = BIFF_GETWORD(q->data) ; d->name = biff_get_text(q->data+4, BIFF_GETWORD(q->data+2)) ; } else /* FIXME: mythical old papyrus spec. */ { d->name = biff_get_text(q->data+1, BIFF_GETBYTE(q->data)) ; d->idx = g_hash_table_size (wb->format_data) + 0x32 ; } /* printf ("Format data : %d == '%s'\n", d->idx, d->name) ; */ g_hash_table_insert (wb->format_data, &d->idx, d) ; } case BIFF_EXTERNCOUNT: /* see S59D7D.HTM */ printf ("%d external references\n", BIFF_GETWORD(q->data)) ; break ; default: switch (q->opcode) { case BIFF_SUPBOOK: /* see S59DEC.HM, but this whole thing seems sketchy : always get 03 00 01 04 */ printf ("SupBook: %d tabs in workbook (FIXME!)\n", BIFF_GETWORD (q->data) ) ; dump (q->data, q->length); break ; default: printf ("Opcode : 0x%x, length 0x%x\n", q->opcode, q->length); /* * dump (q->data, q->length); */ break; } } } ms_biff_query_destroy (q); if (ver) ms_biff_bof_data_destroy (ver); ms_ole_stream_close (stream); } if (wb) { workbook_recalc (wb->gnum_wb); return wb->gnum_wb; } return 0; }