[gnumeric] xls: fix saving of IFERROR and other XL97 functions.



commit b8ce7050e700eece8b5674bc035de69f9f7df62b
Author: Morten Welinder <terra gnome org>
Date:   Wed Apr 10 11:55:18 2013 -0400

    xls: fix saving of IFERROR and other XL97 functions.

 NEWS                             |    3 +-
 plugins/excel/ms-excel-read.c    |   37 ++++++++++++++++
 plugins/excel/ms-formula-write.c |   85 ++++++++++++++++++++------------------
 3 files changed, 84 insertions(+), 41 deletions(-)
---
diff --git a/NEWS b/NEWS
index 1eb43a5..14b9598 100644
--- a/NEWS
+++ b/NEWS
@@ -25,12 +25,13 @@ Morten:
        * Fix problems with R.PSNORM.  [#697293]
        * New function OWENT.
        * New function POCHHAMMER.
-       * Fix import of IFERROR from xls.
+       * Fix import of XL97 functions like IFERROR from xls.
        * Add test sheet for complex number parsing.
        * Improve complex number parsing.
        * Fix error codes for complex number functions.
        * Fix import of non-Excel functions from LO's xls files.
        * Fix problem with xls saving of externnames.
+       * Fix saving of certain XL97 functions like IFERROR.
 
 --------------------------------------------------------------------------
 Gnumeric 1.12.1
diff --git a/plugins/excel/ms-excel-read.c b/plugins/excel/ms-excel-read.c
index f935c85..1d0312f 100644
--- a/plugins/excel/ms-excel-read.c
+++ b/plugins/excel/ms-excel-read.c
@@ -7225,6 +7225,26 @@ excel_read_workbook (GOIOContext *context, WorkbookView *wb_view,
 
 static GSList *formats;
 
+/*
+ * These are special in that they appear to be saved as macros.
+ * Excel will only recognize them when saved as macros: neither
+ * externname as "IFERROR" nor "_xlfn.IFERROR" will work.
+ */
+static const ExcelFuncDesc excel97_func_desc[] = {
+       { 0xff, "_xlfn.AVERAGEIF", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.AVERAGEIFS", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBEKPIMEMBER", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBEMEMBER", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBEMEMBERPROPERTY", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBERANKEDMEMBER", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBESET", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBESETCOUNT", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.CUBEVALUE", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.COUNTIFS", -1, -1, XL_XLM },
+       { 0xff, "_xlfn.IFERROR", 2, 2, XL_XLM, 2, 'V', "VV" },
+       { 0xff, "_xlfn.SUMIFS", -1, -1, XL_XLM }
+};
+
 void
 excel_read_init (void)
 {
@@ -7263,6 +7283,23 @@ excel_read_init (void)
                                     (gpointer)name,
                                     (gpointer)efd);
        }
+
+       for (i = 0; i < (int)G_N_ELEMENTS(excel97_func_desc); i++) {
+               const ExcelFuncDesc *efd = excel97_func_desc + i;
+               const char *excel_name = efd->name;
+               const char *gnm_name = strchr (excel_name, '.') + 1;
+               GnmFunc *func = gnm_func_lookup (gnm_name, NULL);
+
+               /* Fix case.  */
+               if (func)
+                       gnm_name = gnm_func_get_name (func, FALSE);
+
+               g_assert (g_hash_table_lookup (excel_func_by_name, gnm_name) ==
+                         NULL);
+               g_hash_table_insert (excel_func_by_name,
+                                    (gpointer)gnm_name,
+                                    (gpointer)efd);
+       }
 }
 
 void
diff --git a/plugins/excel/ms-formula-write.c b/plugins/excel/ms-formula-write.c
index 0c8b32d..6f43d3f 100644
--- a/plugins/excel/ms-formula-write.c
+++ b/plugins/excel/ms-formula-write.c
@@ -110,7 +110,7 @@ do_excel_write_prep_expr (ExcelWriteState *ewb, GnmExpr const *expr)
                 */
                ef = g_hash_table_lookup (ewb->function_map, func);
                if (ef != NULL)
-                       return;
+                       break;
 
                ef = g_new (ExcelFunc, 1);
                ef->efunc = (func->flags & (GNM_FUNC_IS_PLACEHOLDER |
@@ -119,15 +119,20 @@ do_excel_write_prep_expr (ExcelWriteState *ewb, GnmExpr const *expr)
                        : g_hash_table_lookup (excel_func_by_name,
                                               func->name);
 
-               if (ef->efunc) {
-                       ef->idx = ef->efunc - excel_func_desc;
+               if (ef->efunc && ef->efunc->idx == 0xff) {
+                       /* These functions appear to be saved as macros! */
+                       ef->macro_name = g_strdup (ef->efunc->name);
+                       ef->idx = -1;
+               } else if (ef->efunc) {
                        ef->macro_name = NULL;
+                       ef->idx = ef->efunc->idx;
                } else if (func->flags & GNM_FUNC_IS_WORKBOOK_LOCAL) {
                        ef->macro_name = g_strdup (func->name);
                        ef->idx = -1;
                } else {
-                       g_ptr_array_add (ewb->externnames,
-                                        g_utf8_strup (gnm_func_get_name (func, FALSE), -1));
+                       char *fname =
+                               g_utf8_strup (gnm_func_get_name (func, FALSE), -1);
+                       g_ptr_array_add (ewb->externnames, fname);
                        ef->macro_name = NULL;
                        ef->idx = ewb->externnames->len;
                }
@@ -523,7 +528,7 @@ write_funcall (PolishData *pd, GnmExpr const *expr,
 {
        static guint8 const zeros [12];
 
-       int arg;
+       int arg, min_args, max_args, name_arg = 0;
        gboolean prompt   = FALSE;
        gboolean cmdequiv = FALSE;
        char const *arg_types = NULL;
@@ -531,10 +536,25 @@ write_funcall (PolishData *pd, GnmExpr const *expr,
        GnmFunc *func = expr->func.func;
        ExcelFunc *ef = g_hash_table_lookup (pd->ewb->function_map, func);
        XLOpType arg_type = XL_VAL; /* default */
+       XLOpType func_type;
+       int func_idx;
+       guint8 op_class;
 
        g_return_if_fail (ef != NULL);
 
-       if (ef->efunc == NULL) {
+       if (ef->efunc) {
+               min_args = ef->efunc->min_args;
+               max_args = ef->efunc->max_args;
+               func_idx = ef->efunc->idx;
+               func_type = xl_map_char_to_type (ef->efunc->type);
+               arg_types = ef->efunc->known_args;
+       } else {
+               min_args = max_args = expr->func.argc;
+               func_idx = 0xff;
+               func_type = XL_VAL;  /*Assumption */
+       }
+
+       if (ef->efunc == NULL || ef->efunc->idx == 0xff) {
                if (ef->macro_name != NULL) {
                        push_guint8 (pd, FORMULA_PTG_NAME);
                        push_guint16 (pd, ef->idx);
@@ -556,14 +576,13 @@ write_funcall (PolishData *pd, GnmExpr const *expr,
                                push_guint16 (pd, ef->idx);
                                push_guint16 (pd, 0); /* reserved */
                        }
-
-                       arg_types_holder = guess_arg_types (func);
-                       arg_types = arg_types_holder;
                }
-       } else
-               arg_types = ef->efunc->known_args;
+               name_arg = 1;
+       }
 
-       for (arg = 0; arg < expr->func.argc; arg++)
+       if (!arg_types)
+               arg_types = arg_types_holder = guess_arg_types (func);
+       for (arg = 0; arg < expr->func.argc; arg++) {
                if (ef->efunc != NULL && arg >= ef->efunc->max_args) {
                        go_io_warning (pd->ewb->io_context,
                                _("Too many arguments for function '%s', MS Excel can only handle %d not %d"),
@@ -577,35 +596,21 @@ write_funcall (PolishData *pd, GnmExpr const *expr,
                        }
                        write_node (pd, expr->func.argv[arg], 0, arg_type);
                }
+       }
+       /* If XL requires more arguments than we do
+        * pad the remainder with missing args */
+       for ( ; arg < min_args ; arg++)
+               push_guint8 (pd, FORMULA_PTG_MISSARG);
        g_free (arg_types_holder);
 
-       if (ef->efunc != NULL) {
-               guint8 op_class = xl_get_op_class (pd,
-                       xl_map_char_to_type (ef->efunc->type), target_type);
-
-#if FORMULA_DEBUG > 1
-               g_printerr ("Writing function '%s' as idx %d, args %d\n",
-                           name, ef->u.std.idx, fce->u.std.efunc->num_known_args);
-#endif
-
-               /* If XL requires more arguments than we do
-                * pad the remainder with missing args */
-               for ( ; arg < ef->efunc->min_args ; arg++)
-                       push_guint8 (pd, FORMULA_PTG_MISSARG);
-
-               if (ef->efunc->min_args != ef->efunc->max_args) {
-                       push_guint8  (pd, FORMULA_PTG_FUNC_VAR + op_class);
-                       push_guint8  (pd, arg | (prompt ? 0x80 : 0));
-                       push_guint16 (pd, ef->idx  | (cmdequiv ? 0x8000 : 0));
-               } else {
-                       push_guint8  (pd, FORMULA_PTG_FUNC + op_class);
-                       push_guint16 (pd, ef->idx);
-               }
-       } else { /* Undocumented, assume result is XL_VAL */
-               push_guint8  (pd, FORMULA_PTG_FUNC_VAR +
-                       xl_get_op_class (pd,  XL_VAL, target_type));
-               push_guint8  (pd, (arg + 1)     | (prompt   ? 0x80 : 0));
-               push_guint16 (pd, 0xff          | (cmdequiv ? 0x8000 : 0));
+       op_class = xl_get_op_class (pd, func_type, target_type);
+       if (name_arg || min_args != max_args) {
+               push_guint8  (pd, FORMULA_PTG_FUNC_VAR + op_class);
+               push_guint8  (pd, (arg + name_arg) | (prompt ? 0x80 : 0));
+               push_guint16 (pd, func_idx         | (cmdequiv ? 0x8000 : 0));
+       } else {
+               push_guint8  (pd, FORMULA_PTG_FUNC + op_class);
+               push_guint16 (pd, func_idx);
        }
 }
 


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]