[gnumeric] ssdiff: move comparison engine into its own file.



commit 53e138688d0fc857b6922ed53f4e0576ea80c6af
Author: Morten Welinder <terra gnome org>
Date:   Sat Apr 7 21:30:13 2018 -0400

    ssdiff: move comparison engine into its own file.

 src/Makefile.am  |  160 ++++++++--------
 src/sheet-diff.c |  542 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/sheet-diff.h |   72 +++++++
 src/ssdiff.c     |  579 +++++-------------------------------------------------
 4 files changed, 741 insertions(+), 612 deletions(-)
---
diff --git a/src/Makefile.am b/src/Makefile.am
index e87c6ed..ce654bc 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -104,44 +104,50 @@ libspreadsheet_la_SOURCES =                       \
        gnm-marshalers.c                        \
        application.c                           \
        auto-format.c                           \
-       cell.c                                  \
        cell-draw.c                             \
+       cell.c                                  \
        cellspan.c                              \
        clipboard.c                             \
-       criteria.c                              \
        cmd-edit.c                              \
        collect.c                               \
        colrow.c                                \
-       command-context.c                       \
        command-context-stderr.c                \
+       command-context.c                       \
        commands.c                              \
-       complete.c                              \
        complete-sheet.c                        \
+       complete.c                              \
        complex.c                               \
        consolidate.c                           \
+       criteria.c                              \
        dependent.c                             \
-       expr.c                                  \
        expr-deriv.c                            \
        expr-name.c                             \
+       expr.c                                  \
        file-autoft.c                           \
        format-template.c                       \
-       func.c                                  \
        func-builtin.c                          \
+       func.c                                  \
        gnm-commands-slicer.c                   \
        gnm-datetime.c                          \
        gnm-format.c                            \
        gnm-graph-window.c                      \
-       gnm-pane.c                              \
        gnm-pane-impl.h                         \
+       gnm-pane.c                              \
+       gnm-plugin.c                            \
        gnm-random.c                            \
+       gnm-so-filled.c                         \
+       gnm-so-line.c                           \
+       gnm-so-path.c                           \
+       gnm-so-polygon.c                        \
+       gnumeric-conf.c                         \
        gnumeric-simple-canvas.c                \
        graph.c                                 \
-       gutils.c                                \
        gui-clipboard.c                         \
        gui-file.c                              \
        gui-util.c                              \
-       hlink.c                                 \
+       gutils.c                                \
        history.c                               \
+       hlink.c                                 \
        input-msg.c                             \
        item-bar.c                              \
        item-cursor.c                           \
@@ -152,15 +158,17 @@ libspreadsheet_la_SOURCES =                       \
        mstyle.c                                \
        number-match.c                          \
        outoflinedocs.c                         \
-       parser.y                                \
        parse-util.c                            \
+       parser.y                                \
        pattern.c                               \
        position.c                              \
-       preview-grid.c                          \
        preview-grid-impl.h                     \
+       preview-grid.c                          \
+       print-cell.c                            \
        print-info.c                            \
-       rangefunc.c                             \
+       print.c                                 \
        rangefunc-strings.c                     \
+       rangefunc.c                             \
        ranges.c                                \
        rendered-value.c                        \
        search.c                                \
@@ -170,50 +178,43 @@ libspreadsheet_la_SOURCES =                       \
        sf-dpq.c                                \
        sf-gamma.c                              \
        sf-trig.c                               \
-       sheet.c                                 \
-       sheet-view.c                            \
-       sheet-control.c                         \
-       sheet-control-gui.c                     \
-       sheet-merge.c                           \
        sheet-autofill.c                        \
+       sheet-control-gui.c                     \
+       sheet-control.c                         \
+       sheet-diff.c                            \
        sheet-filter.c                          \
-       sheet-utils.c                           \
-       sheet-object.c                          \
+       sheet-merge.c                           \
        sheet-object-cell-comment.c             \
-       gnm-so-filled.c                         \
-       gnm-so-line.c                           \
-       gnm-so-path.c                           \
-       gnm-so-polygon.c                        \
        sheet-object-component.c                \
        sheet-object-graph.c                    \
        sheet-object-image.c                    \
        sheet-object-widget.c                   \
+       sheet-object.c                          \
        sheet-style.c                           \
-       gnm-plugin.c                            \
+       sheet-utils.c                           \
+       sheet-view.c                            \
+       sheet.c                                 \
        sort.c                                  \
-       stf.c                                   \
        stf-export.c                            \
        stf-parse.c                             \
-       style.c                                 \
+       stf.c                                   \
        style-border.c                          \
        style-color.c                           \
        style-conditions.c                      \
+       style.c                                 \
        undo.c                                  \
        validation.c                            \
-       value.c                                 \
        value-sheet.c                           \
-       workbook.c                              \
-       workbook-cmd-format.c                   \
-       workbook-view.c                         \
-       workbook-control.c                      \
-       wbc-gtk.c                               \
+       value.c                                 \
        wbc-gtk-actions.c                       \
        wbc-gtk-edit.c                          \
+       wbc-gtk.c                               \
+       workbook-cmd-format.c                   \
+       workbook-control.c                      \
+       workbook-view.c                         \
+       workbook.c                              \
        xml-sax-read.c                          \
-       xml-sax-write.c                         \
-       gnumeric-conf.c                         \
-       print.c                                 \
-       print-cell.c
+       xml-sax-write.c
 
 nodist_libspreadsheet_la_SOURCES = gnmresources.c gnmresources.h
 
@@ -224,30 +225,30 @@ libspreadsheet_includedir = $(includedir)/libspreadsheet-@GNUMERIC_API_VER@/spre
 libspreadsheet_include_HEADERS =       \
        application.h                           \
        auto-format.h                           \
-       cell.h                                  \
        cell-draw.h                             \
+       cell.h                                  \
        cellspan.h                              \
        clipboard.h                             \
        cmd-edit.h                              \
        collect.h                               \
        colrow.h                                \
-       command-context.h                       \
        command-context-stderr.h                \
+       command-context.h                       \
        commands.h                              \
-       complete.h                              \
        complete-sheet.h                        \
+       complete.h                              \
        complex.h                               \
        consolidate.h                           \
        criteria.h                              \
        dependent.h                             \
-       expr.h                                  \
        expr-deriv.h                            \
        expr-impl.h                             \
        expr-name.h                             \
+       expr.h                                  \
        file-autoft.h                           \
        format-template.h                       \
-       func.h                                  \
        func-builtin.h                          \
+       func.h                                  \
        gnm-command-impl.h                      \
        gnm-commands-slicer.h                   \
        gnm-data-cache-source.h                 \
@@ -255,26 +256,32 @@ libspreadsheet_include_HEADERS =  \
        gnm-format.h                            \
        gnm-graph-window.h                      \
        gnm-pane.h                              \
+       gnm-plugin.h                            \
        gnm-random.h                            \
        gnm-sheet-slicer.h                      \
+       gnm-so-filled.h                         \
+       gnm-so-line.h                           \
+       gnm-so-path.h                           \
+       gnm-so-polygon.h                        \
        gnm-style-impl.h                        \
-       gnumeric.h                              \
+       gnumeric-conf.h                         \
        gnumeric-fwd.h                          \
        gnumeric-simple-canvas.h                \
+       gnumeric.h                              \
+       go-data-cache-field.h                   \
        go-data-cache-source.h                  \
        go-data-cache.h                         \
-       go-data-cache-field.h                   \
-       go-data-slicer.h                        \
        go-data-slicer-field.h                  \
+       go-data-slicer.h                        \
        go-val.h                                \
        graph.h                                 \
-       gutils.h                                \
        gui-clipboard.h                         \
        gui-file.h                              \
        gui-util.h                              \
-       hlink.h                                 \
-       hlink-impl.h                            \
+       gutils.h                                \
        history.h                               \
+       hlink-impl.h                            \
+       hlink.h                                 \
        input-msg.h                             \
        item-bar.h                              \
        item-cursor.h                           \
@@ -289,9 +296,11 @@ libspreadsheet_include_HEADERS =   \
        pattern.h                               \
        position.h                              \
        preview-grid.h                          \
+       print-cell.h                            \
        print-info.h                            \
-       rangefunc.h                             \
+       print.h                                 \
        rangefunc-strings.h                     \
+       rangefunc.h                             \
        ranges.h                                \
        regression.h                            \
        rendered-value.h                        \
@@ -302,58 +311,51 @@ libspreadsheet_include_HEADERS =  \
        sf-dpq.h                                \
        sf-gamma.h                              \
        sf-trig.h                               \
-       sheet.h                                 \
-       sheet-view.h                            \
-       sheet-control.h                         \
-       sheet-control-priv.h                    \
-       sheet-control-gui.h                     \
-       sheet-control-gui-priv.h                \
-       sheet-merge.h                           \
-       sheet-private.h                         \
        sheet-autofill.h                        \
-       sheet-filter.h                          \
-       sheet-utils.h                           \
+       sheet-control-gui-priv.h                \
+       sheet-control-gui.h                     \
+       sheet-control-priv.h                    \
+       sheet-control.h                         \
+       sheet-diff.h                            \
        sheet-filter-combo.h                    \
-       sheet-object.h                          \
-       sheet-object-impl.h                     \
+       sheet-filter.h                          \
+       sheet-merge.h                           \
        sheet-object-cell-comment.h             \
-       sheet-object-widget-impl.h              \
-       gnm-so-filled.h                         \
-       gnm-so-line.h                           \
-       gnm-so-path.h                           \
-       gnm-so-polygon.h                        \
        sheet-object-component.h                \
        sheet-object-graph.h                    \
        sheet-object-image.h                    \
+       sheet-object-impl.h                     \
+       sheet-object-widget-impl.h              \
        sheet-object-widget.h                   \
+       sheet-object.h                          \
+       sheet-private.h                         \
        sheet-style.h                           \
-       gnm-plugin.h                            \
+       sheet-utils.h                           \
+       sheet-view.h                            \
+       sheet.h                                 \
        sort.h                                  \
-       stf.h                                   \
        stf-export.h                            \
        stf-parse.h                             \
-       style.h                                 \
+       stf.h                                   \
        style-border.h                          \
        style-color.h                           \
        style-conditions.h                      \
        style-font.h                            \
+       style.h                                 \
        undo.h                                  \
-       validation.h                            \
        validation-combo.h                      \
+       validation.h                            \
        value.h                                 \
-       workbook.h                              \
-       workbook-priv.h                         \
+       wbc-gtk-impl.h                          \
+       wbc-gtk.h                               \
        workbook-cmd-format.h                   \
-       workbook-view.h                         \
-       workbook-control.h                      \
        workbook-control-priv.h                 \
-       wbc-gtk.h                               \
-       wbc-gtk-impl.h                          \
+       workbook-control.h                      \
+       workbook-priv.h                         \
+       workbook-view.h                         \
+       workbook.h                              \
        xml-io-version.h                        \
-       xml-sax.h                               \
-       gnumeric-conf.h                         \
-       print.h                                 \
-       print-cell.h
+       xml-sax.h
 
 gnumeric_SOURCES =                             \
        io-context-gtk.c                        \
diff --git a/src/sheet-diff.c b/src/sheet-diff.c
new file mode 100644
index 0000000..0b3e3e5
--- /dev/null
+++ b/src/sheet-diff.c
@@ -0,0 +1,542 @@
+/*
+ * sheet-diff.c: Code for comparing sheets.
+ *
+ * Copyright (C) 2018 Morten Welinder (terra gnome org)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or (at your option) version 3.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
+ * USA
+ */
+#include <gnumeric-config.h>
+#include "gnumeric.h"
+#include "sheet-diff.h"
+#include "sheet.h"
+#include "cell.h"
+#include "expr.h"
+#include "value.h"
+#include "sheet-style.h"
+#include "mstyle.h"
+#include "ranges.h"
+#include "expr-name.h"
+#include "workbook.h"
+#include "workbook-priv.h"
+#include <string.h>
+
+/* ------------------------------------------------------------------------- */
+
+static gboolean
+null_diff_start (G_GNUC_UNUSED GnmDiffState *state)
+{
+       return FALSE;
+}
+
+static void
+null_diff_end (G_GNUC_UNUSED GnmDiffState *state)
+{
+}
+
+static void
+null_sheet_start (G_GNUC_UNUSED GnmDiffState *state,
+                 G_GNUC_UNUSED Sheet const *os,
+                 G_GNUC_UNUSED Sheet const *ns)
+{
+}
+
+static void
+null_sheet_end (G_GNUC_UNUSED GnmDiffState *state)
+{
+}
+
+static void
+null_sheet_order_changed (G_GNUC_UNUSED GnmDiffState *state)
+{
+}
+
+static void
+null_sheet_attr_int_changed (G_GNUC_UNUSED GnmDiffState *state,
+                            G_GNUC_UNUSED const char *name,
+                            G_GNUC_UNUSED int o,
+                            G_GNUC_UNUSED int n)
+{
+}
+
+static void
+null_colrow_changed (G_GNUC_UNUSED GnmDiffState *state,
+                    G_GNUC_UNUSED ColRowInfo const *oc, G_GNUC_UNUSED ColRowInfo const *nc,
+                    G_GNUC_UNUSED gboolean is_cols, G_GNUC_UNUSED int i)
+{
+}
+
+static void
+null_cell_changed (G_GNUC_UNUSED GnmDiffState *state,
+                  G_GNUC_UNUSED GnmCell const *oc, GnmCell const *nc)
+{
+}
+
+static void
+null_style_changed (G_GNUC_UNUSED GnmDiffState *state,
+                   G_GNUC_UNUSED GnmRange const *r,
+                   G_GNUC_UNUSED GnmStyle const *os, G_GNUC_UNUSED GnmStyle const *ns)
+{
+}
+
+static void
+null_name_changed (G_GNUC_UNUSED GnmDiffState *state,
+                  G_GNUC_UNUSED GnmNamedExpr const *on, G_GNUC_UNUSED GnmNamedExpr const *nn)
+{
+
+}
+
+#define DISPATCH(method) (istate->actions->method ? istate->actions->method : null_ ## method)
+
+/* ------------------------------------------------------------------------- */
+
+typedef struct {
+       GnmDiffState *ustate;
+
+       const GnmDiffActions *actions;
+       gboolean diff_found;
+       gboolean error;
+       
+       Sheet *old_sheet, *new_sheet;
+       GnmRange common_range;
+
+       Workbook *old_wb, *new_wb;
+} GnmDiffIState;
+
+/* ------------------------------------------------------------------------- */
+
+static gboolean
+compare_texpr_equal (GnmExprTop const *oe, GnmParsePos const *opp,
+                    GnmExprTop const *ne, GnmParsePos const *npp,
+                    GnmConventions const *convs)
+{
+       char *so, *sn;
+       gboolean eq;
+
+       if (gnm_expr_top_equal (oe, ne))
+               return TRUE;
+
+       // Not equal, but with references to sheets, that is not
+       // necessary.  Compare as strings.
+
+       so = gnm_expr_top_as_string (oe, opp, convs);
+       sn = gnm_expr_top_as_string (ne, npp, convs);
+
+       eq = g_strcmp0 (so, sn) == 0;
+
+       g_free (so);
+       g_free (sn);
+
+       return eq;
+}
+
+static gboolean
+compare_corresponding_cells (GnmCell const *co, GnmCell const *cn)
+{
+       gboolean has_expr = gnm_cell_has_expr (co);
+       gboolean has_value = co->value != NULL;
+
+       if (has_expr != gnm_cell_has_expr (cn))
+               return TRUE;
+       if (has_expr) {
+               GnmParsePos opp, npp;
+               parse_pos_init_cell (&opp, co);
+               parse_pos_init_cell (&npp, cn);
+               return !compare_texpr_equal (co->base.texpr, &opp,
+                                            cn->base.texpr, &npp,
+                                            sheet_get_conventions (cn->base.sheet));
+       }
+
+       if (has_value != (cn->value != NULL))
+               return TRUE;
+       if (has_value)
+               return !(value_equal (co->value, cn->value) &&
+                        go_format_eq (VALUE_FMT (co->value),
+                                      VALUE_FMT (cn->value)));
+
+
+       return FALSE;
+}
+
+static gboolean
+ignore_cell (GnmCell const *cell)
+{
+       if (cell) {
+               if (gnm_cell_has_expr (cell)) {
+                       return gnm_expr_top_is_array_elem (cell->base.texpr,
+                                                          NULL, NULL);
+               } else {
+                       return VALUE_IS_EMPTY (cell->value);
+               }
+       }
+       return FALSE;
+}
+
+static void
+diff_sheets_cells (GnmDiffIState *istate)
+{
+       GPtrArray *old_cells = sheet_cells (istate->old_sheet, NULL);
+       GPtrArray *new_cells = sheet_cells (istate->new_sheet, NULL);
+       size_t io = 0, in = 0;
+
+       // Make code below simpler.
+       g_ptr_array_add (old_cells, NULL);
+       g_ptr_array_add (new_cells, NULL);
+
+       while (TRUE) {
+               GnmCell const *co, *cn;
+
+               while (ignore_cell ((co = g_ptr_array_index (old_cells, io))))
+                       io++;
+
+               while (ignore_cell ((cn = g_ptr_array_index (new_cells, in))))
+                       in++;
+
+               if (co && cn) {
+                       int order = co->pos.row == cn->pos.row
+                               ? co->pos.col - cn->pos.col
+                               : co->pos.row - cn->pos.row;
+                       if (order < 0)
+                               cn = NULL;
+                       else if (order > 0)
+                               co = NULL;
+                       else {
+                               if (compare_corresponding_cells (co, cn)) {
+                                       istate->diff_found = TRUE;
+                                       DISPATCH(cell_changed) (istate->ustate, co, cn);
+                               }
+                               io++, in++;
+                               continue;
+                       }
+               }
+
+               if (co) {
+                       istate->diff_found = TRUE;
+                       DISPATCH(cell_changed) (istate->ustate, co, NULL);
+                       io++;
+               } else if (cn) {
+                       istate->diff_found = TRUE;
+                       DISPATCH(cell_changed) (istate->ustate, NULL, cn);
+                       in++;
+               } else
+                       break;
+       }
+
+       g_ptr_array_free (old_cells, TRUE);
+       g_ptr_array_free (new_cells, TRUE);
+}
+
+static void
+diff_sheets_colrow (GnmDiffIState *istate, gboolean is_cols)
+{
+       ColRowInfo const *old_def =
+               sheet_colrow_get_default (istate->old_sheet, is_cols);
+       ColRowInfo const *new_def =
+               sheet_colrow_get_default (istate->new_sheet, is_cols);
+       int i, U;
+
+       if (!colrow_equal (old_def, new_def)) {
+               istate->diff_found = TRUE;
+               DISPATCH(colrow_changed) (istate->ustate, old_def, new_def, is_cols, -1);
+       }
+
+       U = is_cols
+               ? istate->common_range.end.col
+               : istate->common_range.end.row;
+       for (i = 0; i <= U; i++) {
+               ColRowInfo const *ocr =
+                       sheet_colrow_get (istate->old_sheet, i, is_cols);
+               ColRowInfo const *ncr =
+                       sheet_colrow_get (istate->new_sheet, i, is_cols);
+
+               if (ocr == ncr)
+                       continue; // Considered equal, even if defaults are different
+               if (!ocr) ocr = old_def;
+               if (!ncr) ncr = new_def;
+               if (!colrow_equal (ocr, ncr)) {
+                       istate->diff_found = TRUE;
+                       DISPATCH(colrow_changed) (istate->ustate, ocr, ncr, is_cols, i);
+               }
+       }
+}
+
+#define DO_INT(field,attr)                                             \
+  do {                                                                 \
+         if (istate->old_sheet->field != istate->new_sheet->field) {   \
+                 istate->diff_found = TRUE;                            \
+                 DISPATCH(sheet_attr_int_changed)                      \
+                         (istate->ustate, attr, istate->old_sheet->field, istate->new_sheet->field); \
+         }                                                             \
+  } while (0)
+
+static void
+diff_sheets_attrs (GnmDiffIState *istate)
+{
+       GnmSheetSize const *os = gnm_sheet_get_size (istate->old_sheet);
+       GnmSheetSize const *ns = gnm_sheet_get_size (istate->new_sheet);
+
+       if (os->max_cols != ns->max_cols) {
+               istate->diff_found = TRUE;
+               DISPATCH(sheet_attr_int_changed)
+                       (istate->ustate, "Cols", os->max_cols, ns->max_cols);
+       }
+       if (os->max_rows != ns->max_rows) {
+               istate->diff_found = TRUE;
+               DISPATCH(sheet_attr_int_changed)
+                       (istate->ustate, "Rows", os->max_rows, ns->max_rows);
+       }
+
+       DO_INT (display_formulas, "DisplayFormulas");
+       DO_INT (hide_zero, "HideZero");
+       DO_INT (hide_grid, "HideGrid");
+       DO_INT (hide_col_header, "HideColHeader");
+       DO_INT (hide_row_header, "HideRowHeader");
+       DO_INT (display_outlines, "DisplayOutlines");
+       DO_INT (outline_symbols_below, "OutlineSymbolsBelow");
+       DO_INT (outline_symbols_right, "OutlineSymbolsRight");
+       DO_INT (text_is_rtl, "RTL_Layout");
+       DO_INT (is_protected, "Protected");
+       DO_INT (visibility, "Visibility");
+}
+#undef DO_INT
+
+struct cb_diff_sheets_styles {
+       GnmDiffIState *istate;
+       GnmStyle *old_style;
+};
+
+static void
+cb_diff_sheets_styles_2 (G_GNUC_UNUSED gpointer key,
+                        gpointer sr_, gpointer user_data)
+{
+       GnmStyleRegion *sr = sr_;
+       struct cb_diff_sheets_styles *data = user_data;
+       GnmDiffIState *istate = data->istate;
+       GnmRange r = sr->range;
+
+       if (gnm_style_find_differences (data->old_style, sr->style, TRUE) == 0)
+               return;
+
+       istate->diff_found = TRUE;
+
+       DISPATCH(style_changed) (istate->ustate, &r, data->old_style, sr->style);
+}
+
+static void
+cb_diff_sheets_styles_1 (G_GNUC_UNUSED gpointer key,
+                        gpointer sr_, gpointer user_data)
+{
+       GnmStyleRegion *sr = sr_;
+       struct cb_diff_sheets_styles *data = user_data;
+       GnmDiffIState *istate = data->istate;
+
+       data->old_style = sr->style;
+       sheet_style_range_foreach (istate->new_sheet, &sr->range,
+                                  cb_diff_sheets_styles_2,
+                                  data);
+}
+
+static void
+diff_sheets_styles (GnmDiffIState *istate)
+{
+       struct cb_diff_sheets_styles data;
+
+       data.istate = istate;
+       sheet_style_range_foreach (istate->old_sheet, &istate->common_range,
+                                  cb_diff_sheets_styles_1,
+                                  &data);
+}
+
+static int
+cb_expr_name_by_name (GnmNamedExpr const *a, GnmNamedExpr const *b)
+{
+       return g_strcmp0 (expr_name_name (a), expr_name_name (b));
+}
+
+static void
+diff_names (GnmDiffIState *istate,
+           GnmNamedExprCollection const *onames, GnmNamedExprCollection const *nnames)
+{
+       GSList *old_names = gnm_named_expr_collection_list (onames);
+       GSList *new_names = gnm_named_expr_collection_list (nnames);
+       GSList *lo, *ln;
+       GnmConventions const *convs;
+
+       if (istate->new_sheet)
+               convs = sheet_get_conventions (istate->new_sheet);
+       else
+               // Hmm...  It's not terribly important where we get them
+               convs = sheet_get_conventions (workbook_sheet_by_index (istate->new_wb, 0));
+               
+       old_names = g_slist_sort (old_names, (GCompareFunc)cb_expr_name_by_name);
+       new_names = g_slist_sort (new_names, (GCompareFunc)cb_expr_name_by_name);
+
+       lo = old_names;
+       ln = new_names;
+       while (lo || ln) {
+               GnmNamedExpr const *on = lo ? lo->data : NULL;
+               GnmNamedExpr const *nn = ln ? ln->data : NULL;
+
+               if (!nn || (on && cb_expr_name_by_name (on, nn) < 0)) {
+                       // Old name got removed
+                       istate->diff_found = TRUE;
+                       DISPATCH(name_changed) (istate->ustate, on, nn);
+                       lo = lo->next;
+                       continue;
+               }
+
+               if (!on || (nn && cb_expr_name_by_name (on, nn) > 0)) {
+                       // New name got added
+                       istate->diff_found = TRUE;
+                       DISPATCH(name_changed) (istate->ustate, on, nn);
+                       ln = ln->next;
+                       continue;
+               }
+
+               if (!compare_texpr_equal (on->texpr, &on->pos,
+                                         nn->texpr, &nn->pos,
+                                         convs)) {
+                       istate->diff_found = TRUE;
+                       DISPATCH(name_changed) (istate->ustate, on, nn);
+               }
+
+               lo = lo->next;
+               ln = ln->next;
+       }
+
+       g_slist_free (old_names);
+       g_slist_free (new_names);
+}
+
+static void
+real_diff_sheets (GnmDiffIState *istate, Sheet *old_sheet, Sheet *new_sheet)
+{
+       GnmRange or, nr;
+
+       istate->old_sheet = old_sheet;
+       istate->new_sheet = new_sheet;
+
+       range_init_full_sheet (&or, old_sheet);
+       range_init_full_sheet (&nr, new_sheet);
+       range_intersection (&istate->common_range, &or, &nr);
+
+       diff_sheets_attrs (istate);
+       diff_names (istate, istate->old_sheet->names, istate->new_sheet->names);
+       diff_sheets_colrow (istate, TRUE);
+       diff_sheets_colrow (istate, FALSE);
+       diff_sheets_cells (istate);
+       diff_sheets_styles (istate);
+
+       istate->old_sheet = istate->new_sheet = NULL;
+}
+
+gboolean
+gnm_diff_sheets (const GnmDiffActions *actions, GnmDiffState *state,
+                Sheet *old_sheet, Sheet *new_sheet)
+{
+       GnmDiffIState istate;
+
+       memset (&istate, 0, sizeof (istate));
+       istate.ustate = state;
+       istate.actions = actions;
+       istate.diff_found = FALSE;
+       istate.error = FALSE;
+
+       real_diff_sheets (&istate, old_sheet, new_sheet);
+
+       return istate.diff_found;
+}
+
+static void
+real_diff_workbooks (GnmDiffIState *istate,
+                    Workbook *old_wb, Workbook *new_wb)
+{
+       int last_index = -1;
+       int i, count;
+       gboolean sheet_order_changed = FALSE;
+
+       istate->old_wb = old_wb;
+       istate->new_wb = new_wb;
+
+       if (DISPATCH(diff_start) (istate->ustate)) {
+               istate->error = TRUE;
+               return;
+       }
+
+       diff_names (istate, old_wb->names, new_wb->names);
+
+       // This doesn't handle sheet renames very well, but simply considers
+       // that a sheet deletion and a sheet insert.
+       count = workbook_sheet_count (old_wb);
+       for (i = 0; i < count; i++) {
+               Sheet *old_sheet = workbook_sheet_by_index (old_wb, i);
+               Sheet *new_sheet = workbook_sheet_by_name (new_wb,
+                                                          old_sheet->name_unquoted);
+               DISPATCH(sheet_start) (istate->ustate, old_sheet, new_sheet);
+
+               if (new_sheet) {
+                       if (new_sheet->index_in_wb < last_index)
+                               sheet_order_changed = TRUE;
+                       last_index = new_sheet->index_in_wb;
+
+                       real_diff_sheets (istate, old_sheet, new_sheet);
+               } else
+                       istate->diff_found = TRUE;
+
+               DISPATCH(sheet_end) (istate->ustate);
+       }
+
+       count = workbook_sheet_count (new_wb);
+       for (i = 0; i < count; i++) {
+               Sheet *new_sheet = workbook_sheet_by_index (new_wb, i);
+               Sheet *old_sheet = workbook_sheet_by_name (old_wb,
+                                                          new_sheet->name_unquoted);
+               if (old_sheet)
+                       ; // Nothing -- already done above.
+               else {
+                       istate->diff_found = TRUE;
+                       DISPATCH(sheet_start) (istate->ustate, old_sheet, new_sheet);
+                       DISPATCH(sheet_end) (istate->ustate);
+               }
+       }
+
+       if (sheet_order_changed) {
+               istate->diff_found = TRUE;
+               DISPATCH(sheet_order_changed) (istate->ustate);
+       }
+
+       DISPATCH(diff_end) (istate->ustate);
+}
+
+int
+gnm_diff_workbooks (const GnmDiffActions *actions, GnmDiffState *state,
+                   Workbook *old_wb, Workbook *new_wb)
+{
+       GnmDiffIState istate;
+
+       memset (&istate, 0, sizeof (istate));
+       istate.ustate = state;
+       istate.actions = actions;
+       istate.diff_found = FALSE;
+       istate.error = FALSE;
+
+       real_diff_workbooks (&istate, old_wb, new_wb);
+
+       return istate.error
+               ? 2
+               : (istate.diff_found
+                  ? 1
+                  : 0);
+}
diff --git a/src/sheet-diff.h b/src/sheet-diff.h
new file mode 100644
index 0000000..726accf
--- /dev/null
+++ b/src/sheet-diff.h
@@ -0,0 +1,72 @@
+#ifndef GNM_SHEET_DIFF_H
+#define GNM_SHEET_DIFF_H
+
+#include "gnumeric.h"
+
+G_BEGIN_DECLS
+
+typedef struct GnmDiffState_ GnmDiffState;
+
+typedef struct {
+       // Start comparison of two workbooks.
+       gboolean (*diff_start) (GnmDiffState *state);
+
+       // Finish comparison started with above.
+       void (*diff_end) (GnmDiffState *state);
+
+       // Clean up allocations
+       // This is not actually called by code here
+       void (*dtor) (GnmDiffState *state);
+
+       /* ------------------------------ */
+
+       // Start looking at a sheet.  Either sheet might be NULL.
+       void (*sheet_start) (GnmDiffState *state,
+                            Sheet const *os, Sheet const *ns);
+
+       // Finish sheet started with above.
+       void (*sheet_end) (GnmDiffState *state);
+
+       // The order of sheets has changed.
+       void (*sheet_order_changed) (GnmDiffState *state);
+
+       // An integer attribute of the sheet has changed.
+       void (*sheet_attr_int_changed) (GnmDiffState *state, const char *name,
+                                       int o, int n);
+
+       /* ------------------------------ */
+
+       // Column or Row information changed
+       void (*colrow_changed) (GnmDiffState *state,
+                               ColRowInfo const *oc, ColRowInfo const *nc,
+                               gboolean is_cols, int i);
+
+       /* ------------------------------ */
+
+       // A cell was changed/added/removed.
+       void (*cell_changed) (GnmDiffState *state,
+                             GnmCell const *oc, GnmCell const *nc);
+
+       /* ------------------------------ */
+
+       // The style of an area was changed.
+       void (*style_changed) (GnmDiffState *state, GnmRange const *r,
+                              GnmStyle const *os, GnmStyle const *ns);
+
+       /* ------------------------------ */
+
+       // A defined name was changed
+       void (*name_changed) (GnmDiffState *state,
+                             GnmNamedExpr const *on, GnmNamedExpr const *nn);
+} GnmDiffActions;
+
+
+gboolean gnm_diff_sheets (const GnmDiffActions *actions, GnmDiffState *state,
+                         Sheet *old_sheet, Sheet *new_sheet);
+
+int gnm_diff_workbooks (const GnmDiffActions *actions, GnmDiffState *state,
+                       Workbook *old_wb, Workbook *new_wb);
+
+G_END_DECLS
+
+#endif /* GNM_SHEET_DIFF_H */
diff --git a/src/ssdiff.c b/src/ssdiff.c
index b6cb81e..2205fd0 100644
--- a/src/ssdiff.c
+++ b/src/ssdiff.c
@@ -31,7 +31,7 @@
 #include "hlink.h"
 #include "input-msg.h"
 #include "expr-name.h"
-#include "workbook-priv.h"
+#include "sheet-diff.h"
 #include <gsf/gsf-libxml.h>
 #include <gsf/gsf-output-stdio.h>
 #include <gsf/gsf-input.h>
@@ -80,60 +80,6 @@ static const GOptionEntry ssdiff_options [] = {
 
 /* -------------------------------------------------------------------------- */
 
-typedef struct GnmDiffState_ GnmDiffState;
-
-typedef struct {
-       // Start comparison of two workbooks.
-       gboolean (*diff_start) (GnmDiffState *state);
-
-       // Finish comparison started with above.
-       void (*diff_end) (GnmDiffState *state);
-
-       // Clean up allocations
-       void (*dtor) (GnmDiffState *state);
-
-       /* ------------------------------ */
-
-       // Start looking at a sheet.  Either sheet might be NULL.
-       void (*sheet_start) (GnmDiffState *state,
-                            Sheet const *os, Sheet const *ns);
-
-       // Finish sheet started with above.
-       void (*sheet_end) (GnmDiffState *state);
-
-       // The order of sheets has changed.
-       void (*sheet_order_changed) (GnmDiffState *state);
-
-       // An integer attribute of the sheet has changed.
-       void (*sheet_attr_int_changed) (GnmDiffState *state, const char *name,
-                                       int o, int n);
-
-       /* ------------------------------ */
-
-       // Column or Row information changed
-       void (*colrow_changed) (GnmDiffState *state,
-                               ColRowInfo const *oc, ColRowInfo const *nc,
-                               gboolean is_cols, int i);
-
-       /* ------------------------------ */
-
-       // A cell was changed/added/removed.
-       void (*cell_changed) (GnmDiffState *state,
-                             GnmCell const *oc, GnmCell const *nc);
-
-       /* ------------------------------ */
-
-       // The style of an area was changed.
-       void (*style_changed) (GnmDiffState *state, GnmRange const *r,
-                              GnmStyle const *os, GnmStyle const *ns);
-
-       /* ------------------------------ */
-
-       // A defined name was changed
-       void (*name_changed) (GnmDiffState *state,
-                             GnmNamedExpr const *on, GnmNamedExpr const *nn);
-} GnmDiffActions;
-
 typedef struct {
        char *url;
        GsfInput *input;
@@ -145,81 +91,20 @@ struct GnmDiffState_ {
        GOIOContext *ioc;
        GnmDiffStateFile old, new;
 
-       const GnmDiffActions *actions;
-
-       gboolean diff_found;
-
        GsfOutput *output;
 
-       // Valid when comparing sheets
-       Sheet *old_sheet, *new_sheet;
-       GnmRange common_range;
-
        // The following for xml mode.
        GsfXMLOut *xml;
        const char *xml_section;
        GnmConventions *xml_convs;
 
        // The following for highlight mode.
+       Sheet *highlight_sheet;
        GnmDiffStateFile highlight;
        GOFileSaver const *highlight_fs;
        GnmStyle *highlight_style;
 };
 
-static gboolean
-null_diff_start (G_GNUC_UNUSED GnmDiffState *state)
-{
-       return FALSE;
-}
-
-static void
-null_diff_end (G_GNUC_UNUSED GnmDiffState *state)
-{
-}
-
-static void
-null_dtor (G_GNUC_UNUSED GnmDiffState *state)
-{
-}
-
-static void
-null_sheet_start (G_GNUC_UNUSED GnmDiffState *state,
-                 G_GNUC_UNUSED Sheet const *os,
-                 G_GNUC_UNUSED Sheet const *ns)
-{
-}
-
-static void
-null_sheet_end (G_GNUC_UNUSED GnmDiffState *state)
-{
-}
-
-static void
-null_sheet_order_changed (G_GNUC_UNUSED GnmDiffState *state)
-{
-}
-
-static void
-null_sheet_attr_int_changed (G_GNUC_UNUSED GnmDiffState *state,
-                            G_GNUC_UNUSED const char *name,
-                            G_GNUC_UNUSED int o,
-                            G_GNUC_UNUSED int n)
-{
-}
-
-static void
-null_colrow_changed (G_GNUC_UNUSED GnmDiffState *state,
-                    G_GNUC_UNUSED ColRowInfo const *oc, G_GNUC_UNUSED ColRowInfo const *nc,
-                    G_GNUC_UNUSED gboolean is_cols, G_GNUC_UNUSED int i)
-{
-}
-
-static void
-null_name_changed (G_GNUC_UNUSED GnmDiffState *state,
-                  G_GNUC_UNUSED GnmNamedExpr const *on, G_GNUC_UNUSED GnmNamedExpr const *nn)
-{
-}
-
 /* -------------------------------------------------------------------------- */
 
 static gboolean
@@ -353,17 +238,13 @@ def_name_changed (GnmDiffState *state,
 }
 
 static const GnmDiffActions default_actions = {
-       null_diff_start,
-       null_diff_end,
-       null_dtor,
-       def_sheet_start,
-       null_sheet_end,
-       def_sheet_order_changed,
-       def_sheet_attr_int_changed,
-       def_colrow_changed,
-       def_cell_changed,
-       def_style_changed,
-       def_name_changed,
+       .sheet_start = def_sheet_start,
+       .sheet_order_changed = def_sheet_order_changed,
+       .sheet_attr_int_changed = def_sheet_attr_int_changed,
+       .colrow_changed = def_colrow_changed,
+       .cell_changed = def_cell_changed,
+       .style_changed = def_style_changed,
+       .name_changed = def_name_changed,
 };
 
 /* -------------------------------------------------------------------------- */
@@ -846,17 +727,16 @@ xml_name_changed (GnmDiffState *state,
 }
 
 static const GnmDiffActions xml_actions = {
-       xml_diff_start,
-       xml_diff_end,
-       xml_dtor,
-       xml_sheet_start,
-       xml_sheet_end,
-       null_sheet_order_changed,
-       xml_sheet_attr_int_changed,
-       xml_colrow_changed,
-       xml_cell_changed,
-       xml_style_changed,
-       xml_name_changed,
+       .diff_start = xml_diff_start,
+       .diff_end = xml_diff_end,
+       .dtor = xml_dtor,
+       .sheet_start = xml_sheet_start,
+       .sheet_end = xml_sheet_end,
+       .sheet_attr_int_changed = xml_sheet_attr_int_changed,
+       .colrow_changed = xml_colrow_changed,
+       .cell_changed = xml_cell_changed,
+       .style_changed = xml_style_changed,
+       .name_changed = xml_name_changed,
 };
 
 /* -------------------------------------------------------------------------- */
@@ -908,11 +788,26 @@ highlight_dtor (GnmDiffState *state)
 }
 
 static void
-highlight_apply (GnmDiffState *state, const GnmRange *r)
+highlight_sheet_start (GnmDiffState *state,
+                      G_GNUC_UNUSED Sheet const *os, Sheet const *ns)
 {
        // We want the highlight sheet corresponding to new_sheet.
-       Sheet *sheet = workbook_sheet_by_index (state->highlight.wb,
-                                               state->new_sheet->index_in_wb);
+       state->highlight_sheet = ns
+               ? workbook_sheet_by_index (state->highlight.wb, ns->index_in_wb)
+               : NULL;
+}
+
+static void
+highlight_sheet_end (GnmDiffState *state)
+{
+       state->highlight_sheet = NULL;
+}
+
+static void
+highlight_apply (GnmDiffState *state, const GnmRange *r)
+{
+       Sheet *sheet = state->highlight_sheet;
+
        g_return_if_fail (IS_SHEET (sheet));
 
        gnm_style_ref (state->highlight_style);
@@ -937,347 +832,17 @@ highlight_style_changed (GnmDiffState *state, GnmRange const *r,
 
 
 static const GnmDiffActions highlight_actions = {
-       highlight_diff_start,
-       highlight_diff_end,
-       highlight_dtor,
-       null_sheet_start,
-       null_sheet_end,
-       null_sheet_order_changed,
-       null_sheet_attr_int_changed,
-       null_colrow_changed,
-       highlight_cell_changed,
-       highlight_style_changed,
-       null_name_changed,
+       .diff_start = highlight_diff_start,
+       .diff_end = highlight_diff_end,
+       .dtor = highlight_dtor,
+       .sheet_start = highlight_sheet_start,
+       .sheet_end = highlight_sheet_end,
+       .cell_changed = highlight_cell_changed,
+       .style_changed = highlight_style_changed,
 };
 
 /* -------------------------------------------------------------------------- */
 
-static gboolean
-compare_texpr_equal (GnmExprTop const *oe, GnmParsePos const *opp,
-                    GnmExprTop const *ne, GnmParsePos const *npp,
-                    GnmConventions const *convs)
-{
-       char *so, *sn;
-       gboolean eq;
-
-       if (gnm_expr_top_equal (oe, ne))
-               return TRUE;
-
-       // Not equal, but with references to sheets, that is not
-       // necessary.  Compare as strings.
-
-       so = gnm_expr_top_as_string (oe, opp, convs);
-       sn = gnm_expr_top_as_string (ne, npp, convs);
-
-       eq = g_strcmp0 (so, sn) == 0;
-
-       g_free (so);
-       g_free (sn);
-
-       return eq;
-}
-
-static gboolean
-compare_corresponding_cells (GnmCell const *co, GnmCell const *cn)
-{
-       gboolean has_expr = gnm_cell_has_expr (co);
-       gboolean has_value = co->value != NULL;
-
-       if (has_expr != gnm_cell_has_expr (cn))
-               return TRUE;
-       if (has_expr) {
-               GnmParsePos opp, npp;
-               parse_pos_init_cell (&opp, co);
-               parse_pos_init_cell (&npp, cn);
-               return !compare_texpr_equal (co->base.texpr, &opp,
-                                            cn->base.texpr, &npp,
-                                            sheet_get_conventions (cn->base.sheet));
-       }
-
-       if (has_value != (cn->value != NULL))
-               return TRUE;
-       if (has_value)
-               return !(value_equal (co->value, cn->value) &&
-                        go_format_eq (VALUE_FMT (co->value),
-                                      VALUE_FMT (cn->value)));
-
-
-       return FALSE;
-}
-
-static gboolean
-ignore_cell (GnmCell const *cell)
-{
-       if (cell) {
-               if (gnm_cell_has_expr (cell)) {
-                       return gnm_expr_top_is_array_elem (cell->base.texpr,
-                                                          NULL, NULL);
-               } else {
-                       return VALUE_IS_EMPTY (cell->value);
-               }
-       }
-       return FALSE;
-}
-
-static void
-diff_sheets_cells (GnmDiffState *state)
-{
-       GPtrArray *old_cells = sheet_cells (state->old_sheet, NULL);
-       GPtrArray *new_cells = sheet_cells (state->new_sheet, NULL);
-       size_t io = 0, in = 0;
-
-       // Make code below simpler.
-       g_ptr_array_add (old_cells, NULL);
-       g_ptr_array_add (new_cells, NULL);
-
-       while (TRUE) {
-               GnmCell const *co, *cn;
-
-               while (ignore_cell ((co = g_ptr_array_index (old_cells, io))))
-                       io++;
-
-               while (ignore_cell ((cn = g_ptr_array_index (new_cells, in))))
-                       in++;
-
-               if (co && cn) {
-                       int order = co->pos.row == cn->pos.row
-                               ? co->pos.col - cn->pos.col
-                               : co->pos.row - cn->pos.row;
-                       if (order < 0)
-                               cn = NULL;
-                       else if (order > 0)
-                               co = NULL;
-                       else {
-                               if (compare_corresponding_cells (co, cn)) {
-                                       state->diff_found = TRUE;
-                                       state->actions->cell_changed (state, co, cn);
-                               }
-                               io++, in++;
-                               continue;
-                       }
-               }
-
-               if (co) {
-                       state->diff_found = TRUE;
-                       state->actions->cell_changed (state, co, NULL);
-                       io++;
-               } else if (cn) {
-                       state->diff_found = TRUE;
-                       state->actions->cell_changed (state, NULL, cn);
-                       in++;
-               } else
-                       break;
-       }
-
-       g_ptr_array_free (old_cells, TRUE);
-       g_ptr_array_free (new_cells, TRUE);
-}
-
-static void
-diff_sheets_colrow (GnmDiffState *state, gboolean is_cols)
-{
-       ColRowInfo const *old_def =
-               sheet_colrow_get_default (state->old_sheet, is_cols);
-       ColRowInfo const *new_def =
-               sheet_colrow_get_default (state->new_sheet, is_cols);
-       int i, U;
-
-       if (!colrow_equal (old_def, new_def)) {
-               state->diff_found = TRUE;
-               state->actions->colrow_changed (state, old_def, new_def, is_cols, -1);
-       }
-
-       U = is_cols
-               ? state->common_range.end.col
-               : state->common_range.end.row;
-       for (i = 0; i <= U; i++) {
-               ColRowInfo const *ocr =
-                       sheet_colrow_get (state->old_sheet, i, is_cols);
-               ColRowInfo const *ncr =
-                       sheet_colrow_get (state->new_sheet, i, is_cols);
-
-               if (ocr == ncr)
-                       continue; // Considered equal, even if defaults are different
-               if (!ocr) ocr = old_def;
-               if (!ncr) ncr = new_def;
-               if (!colrow_equal (ocr, ncr)) {
-                       state->diff_found = TRUE;
-                       state->actions->colrow_changed (state, ocr, ncr, is_cols, i);
-               }
-       }
-}
-
-#define DO_INT(field,attr)                                             \
-  do {                                                                 \
-         if (state->old_sheet->field != state->new_sheet->field) {     \
-                 state->diff_found = TRUE;                             \
-                 state->actions->sheet_attr_int_changed                \
-                         (state, attr, state->old_sheet->field, state->new_sheet->field); \
-         }                                                             \
-  } while (0)
-
-static void
-diff_sheets_attrs (GnmDiffState *state)
-{
-       GnmSheetSize const *os = gnm_sheet_get_size (state->old_sheet);
-       GnmSheetSize const *ns = gnm_sheet_get_size (state->new_sheet);
-
-       if (os->max_cols != ns->max_cols) {
-               state->diff_found = TRUE;
-               state->actions->sheet_attr_int_changed
-                       (state, "Cols", os->max_cols, ns->max_cols);
-       }
-       if (os->max_rows != ns->max_rows) {
-               state->diff_found = TRUE;
-               state->actions->sheet_attr_int_changed
-                       (state, "Rows", os->max_rows, ns->max_rows);
-       }
-
-       DO_INT (display_formulas, "DisplayFormulas");
-       DO_INT (hide_zero, "HideZero");
-       DO_INT (hide_grid, "HideGrid");
-       DO_INT (hide_col_header, "HideColHeader");
-       DO_INT (hide_row_header, "HideRowHeader");
-       DO_INT (display_outlines, "DisplayOutlines");
-       DO_INT (outline_symbols_below, "OutlineSymbolsBelow");
-       DO_INT (outline_symbols_right, "OutlineSymbolsRight");
-       DO_INT (text_is_rtl, "RTL_Layout");
-       DO_INT (is_protected, "Protected");
-       DO_INT (visibility, "Visibility");
-}
-#undef DO_INT
-
-struct cb_diff_sheets_styles {
-       GnmDiffState *state;
-       GnmStyle *old_style;
-};
-
-static void
-cb_diff_sheets_styles_2 (G_GNUC_UNUSED gpointer key,
-                        gpointer sr_, gpointer user_data)
-{
-       GnmStyleRegion *sr = sr_;
-       struct cb_diff_sheets_styles *data = user_data;
-       GnmDiffState *state = data->state;
-       GnmRange r = sr->range;
-
-       if (gnm_style_find_differences (data->old_style, sr->style, TRUE) == 0)
-               return;
-
-       state->diff_found = TRUE;
-
-       state->actions->style_changed (state, &r, data->old_style, sr->style);
-}
-
-static void
-cb_diff_sheets_styles_1 (G_GNUC_UNUSED gpointer key,
-                        gpointer sr_, gpointer user_data)
-{
-       GnmStyleRegion *sr = sr_;
-       struct cb_diff_sheets_styles *data = user_data;
-       GnmDiffState *state = data->state;
-
-       data->old_style = sr->style;
-       sheet_style_range_foreach (state->new_sheet, &sr->range,
-                                  cb_diff_sheets_styles_2,
-                                  data);
-}
-
-static void
-diff_sheets_styles (GnmDiffState *state)
-{
-       struct cb_diff_sheets_styles data;
-
-       data.state = state;
-       sheet_style_range_foreach (state->old_sheet, &state->common_range,
-                                  cb_diff_sheets_styles_1,
-                                  &data);
-}
-
-static int
-cb_expr_name_by_name (GnmNamedExpr const *a, GnmNamedExpr const *b)
-{
-       return g_strcmp0 (expr_name_name (a), expr_name_name (b));
-}
-
-static void
-diff_names (GnmDiffState *state,
-           GnmNamedExprCollection const *onames, GnmNamedExprCollection const *nnames)
-{
-       GSList *old_names = gnm_named_expr_collection_list (onames);
-       GSList *new_names = gnm_named_expr_collection_list (nnames);
-       GSList *lo, *ln;
-       GnmConventions const *convs;
-
-       if (state->new_sheet)
-               convs = sheet_get_conventions (state->new_sheet);
-       else
-               // Hmm...  It's not terribly important where we get them
-               convs = sheet_get_conventions (workbook_sheet_by_index (state->new.wb, 0));
-               
-       old_names = g_slist_sort (old_names, (GCompareFunc)cb_expr_name_by_name);
-       new_names = g_slist_sort (new_names, (GCompareFunc)cb_expr_name_by_name);
-
-       lo = old_names;
-       ln = new_names;
-       while (lo || ln) {
-               GnmNamedExpr const *on = lo ? lo->data : NULL;
-               GnmNamedExpr const *nn = ln ? ln->data : NULL;
-
-               if (!nn || (on && cb_expr_name_by_name (on, nn) < 0)) {
-                       // Old name got removed
-                       state->diff_found = TRUE;
-                       state->actions->name_changed (state, on, nn);
-                       lo = lo->next;
-                       continue;
-               }
-
-               if (!on || (nn && cb_expr_name_by_name (on, nn) > 0)) {
-                       // New name got added
-                       state->diff_found = TRUE;
-                       state->actions->name_changed (state, on, nn);
-                       ln = ln->next;
-                       continue;
-               }
-
-               if (!compare_texpr_equal (on->texpr, &on->pos,
-                                         nn->texpr, &nn->pos,
-                                         convs)) {
-                       state->diff_found = TRUE;
-                       state->actions->name_changed (state, on, nn);
-               }
-
-               lo = lo->next;
-               ln = ln->next;
-       }
-
-       g_slist_free (old_names);
-       g_slist_free (new_names);
-}
-
-
-static void
-diff_sheets (GnmDiffState *state, Sheet *old_sheet, Sheet *new_sheet)
-{
-       GnmRange or, nr;
-
-       state->old_sheet = old_sheet;
-       state->new_sheet = new_sheet;
-
-       range_init_full_sheet (&or, old_sheet);
-       range_init_full_sheet (&nr, new_sheet);
-       range_intersection (&state->common_range, &or, &nr);
-
-       diff_sheets_attrs (state);
-       diff_names (state, state->old_sheet->names, state->new_sheet->names);
-       diff_sheets_colrow (state, TRUE);
-       diff_sheets_colrow (state, FALSE);
-       diff_sheets_cells (state);
-       diff_sheets_styles (state);
-
-       state->old_sheet = state->new_sheet = NULL;
-}
-
 static int
 diff (char const *oldfilename, char const *newfilename,
       GOIOContext *ioc,
@@ -1285,15 +850,11 @@ diff (char const *oldfilename, char const *newfilename,
 {
        GnmDiffState state;
        int res = 0;
-       int i, count;
-       gboolean sheet_order_changed = FALSE;
-       int last_index = -1;
        GnmLocale *locale;
 
        locale = gnm_push_C_locale ();
 
        memset (&state, 0, sizeof (state));
-       state.actions = actions;
        state.ioc = ioc;
        state.output = output;
 
@@ -1304,63 +865,15 @@ diff (char const *oldfilename, char const *newfilename,
 
        /* ---------------------------------------- */
 
-       if (state.actions->diff_start (&state))
-               goto error;
-
-       diff_names (&state, state.old.wb->names, state.new.wb->names);
-
-       // This doesn't handle sheet renames very well, but simply considers
-       // that a sheet deletion and a sheet insert.
-       count = workbook_sheet_count (state.old.wb);
-       for (i = 0; i < count; i++) {
-               Sheet *old_sheet = workbook_sheet_by_index (state.old.wb, i);
-               Sheet *new_sheet = workbook_sheet_by_name (state.new.wb,
-                                                          old_sheet->name_unquoted);
-               state.actions->sheet_start (&state, old_sheet, new_sheet);
-
-               if (new_sheet) {
-                       if (new_sheet->index_in_wb < last_index)
-                               sheet_order_changed = TRUE;
-                       last_index = new_sheet->index_in_wb;
-
-                       diff_sheets (&state, old_sheet, new_sheet);
-               } else
-                       state.diff_found = TRUE;
-
-               state.actions->sheet_end (&state);
-       }
-
-       count = workbook_sheet_count (state.new.wb);
-       for (i = 0; i < count; i++) {
-               Sheet *new_sheet = workbook_sheet_by_index (state.new.wb, i);
-               Sheet *old_sheet = workbook_sheet_by_name (state.old.wb,
-                                                          new_sheet->name_unquoted);
-               if (old_sheet)
-                       ; // Nothing -- already done above.
-               else {
-                       state.diff_found = TRUE;
-                       state.actions->sheet_start (&state, old_sheet, new_sheet);
-                       state.actions->sheet_end (&state);
-               }
-       }
-
-       if (sheet_order_changed) {
-               state.diff_found = TRUE;
-               state.actions->sheet_order_changed (&state);
-       }
-
-       state.actions->diff_end (&state);
+       res = gnm_diff_workbooks (actions, &state, state.old.wb, state.new.wb);
 
 out:
        clear_file_state (&state.old);
        clear_file_state (&state.new);
-       state.actions->dtor (&state);
+       actions->dtor (&state);
 
        gnm_pop_C_locale (locale);
 
-       if (res == 0)
-               res = state.diff_found ? 1 : 0;
-
        return res;
 
 error:



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