+/* A value to be kept in the hash table for cache purposes. */
+struct cache_datum
+{
+ struct hmap_node node;
+
+ /* The cell's row. */
+ int row;
+
+ /* The cell's column. */
+ int col;
+
+ /* The value of the cell. */
+ char *value;
+};
+
+static void
+gnumeric_destroy (struct spreadsheet *s)
+{
+ struct gnumeric_reader *r = (struct gnumeric_reader *) s;
+
+ int i;
+
+ for (i = 0; i < r->n_sheets; ++i)
+ {
+ xmlFree (r->spreadsheet.sheets[i].name);
+ }
+
+ if (s->dict)
+ dict_unref (s->dict);
+
+ free (r->spreadsheet.sheets);
+ state_data_destroy (&r->msd);
+
+ free (s->file_name);
+
+ struct cache_datum *cell;
+ struct cache_datum *next;
+ HMAP_FOR_EACH_SAFE (cell, next, struct cache_datum, node, &r->cache)
+ {
+ free (cell->value);
+ free (cell);
+ }
+
+ hmap_destroy (&r->cache);
+
+ free (r);
+}
+
+
+static const char *
+gnumeric_get_sheet_name (struct spreadsheet *s, int n)
+{
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+ assert (n < gr->n_sheets);
+
+ return gr->spreadsheet.sheets[n].name;
+}
+
+
+static void process_node (struct gnumeric_reader *r, struct state_data *sd);
+
+
+static int
+gnumeric_get_sheet_n_sheets (struct spreadsheet *s)
+{
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+
+ int ret;
+ while (1 == (ret = xmlTextReaderRead (gr->msd.xtr)))
+ {
+ process_node (gr, &gr->msd);
+ }
+
+ return gr->n_sheets;
+}
+
+
+static char *
+gnumeric_get_sheet_range (struct spreadsheet *s, int n)
+{
+ int ret;
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+
+ while ((gr->spreadsheet.sheets[n].last_col == -1)
+ &&
+ (1 == (ret = xmlTextReaderRead (gr->msd.xtr))))
+ {
+ process_node (gr, &gr->msd);
+ }
+
+ assert (n < gr->n_sheets);
+ return create_cell_range (
+ gr->spreadsheet.sheets[n].first_col,
+ gr->spreadsheet.sheets[n].first_row,
+ gr->spreadsheet.sheets[n].last_col,
+ gr->spreadsheet.sheets[n].last_row);
+}
+
+
+static unsigned int
+gnumeric_get_sheet_n_rows (struct spreadsheet *s, int n)
+{
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+
+ while ((gr->spreadsheet.sheets[n].last_col == -1)
+ &&
+ (1 == xmlTextReaderRead (gr->msd.xtr)))
+ {
+ process_node (gr, &gr->msd);
+ }
+
+ assert (n < gr->n_sheets);
+ return gr->spreadsheet.sheets[n].last_row + 1;
+}
+
+static unsigned int
+gnumeric_get_sheet_n_columns (struct spreadsheet *s, int n)
+{
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+
+ while ((gr->spreadsheet.sheets[n].last_col == -1)
+ &&
+ (1 == xmlTextReaderRead (gr->msd.xtr)))
+ {
+ process_node (gr, &gr->msd);
+ }
+
+ assert (n < gr->n_sheets);
+ return gr->spreadsheet.sheets[n].last_col + 1;
+}
+
+static struct gnumeric_reader *
+gnumeric_reopen (struct gnumeric_reader *r, const char *filename, bool show_errors);
+
+
+static char *
+gnumeric_get_sheet_cell (struct spreadsheet *s, int n, int row, int column)
+{
+ struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
+
+ /* See if this cell is in the cache. If it is, then use it. */
+ if (use_cache)
+ {
+ struct cache_datum *lookup = NULL;
+ unsigned int hash = hash_int (row, 0);
+ hash = hash_int (column, hash);
+
+ HMAP_FOR_EACH_WITH_HASH (lookup, struct cache_datum, node, hash,
+ &gr->cache)
+ {
+ if (lookup->row == row && lookup->col == column)
+ {
+ break;
+ }
+ }
+ if (lookup)
+ {
+ return strdup (lookup->value);
+ }
+ }
+
+ struct state_data sd;
+
+ sd.state = STATE_PRE_INIT;
+ sd.current_sheet = -1;
+ sd.row = -1;
+ sd.col = -1;
+ sd.min_col = 0;
+ sd.gz = gzopen (s->file_name, "r");
+
+ sd.xtr = xmlReaderForIO ((xmlInputReadCallback) gzread,
+ (xmlInputCloseCallback) gzclose,
+ sd.gz,
+ NULL, NULL,
+ 0);
+
+
+ gr->target_sheet_name = NULL;
+
+ int current_row = -1;
+ int current_col = -1;
+
+ /* Spool to the target cell, caching values of cells as they are encountered. */
+ for (int ret = 1; ret; )
+ {
+ while ((ret = xmlTextReaderRead (sd.xtr)))
+ {
+ process_node (gr, &sd);
+ if (sd.state == STATE_CELL)
+ {
+ if (sd.current_sheet == n)
+ {
+ current_row = sd.row;
+ current_col = sd.col;
+ break;
+ }
+ }
+ }
+ if (current_row >= row && current_col >= column - 1)
+ break;
+
+ while ((ret = xmlTextReaderRead (sd.xtr)))
+ {
+ process_node (gr, &sd);
+ if (sd.node_type == XML_READER_TYPE_TEXT)
+ break;
+ }
+
+ if (use_cache)
+ {
+ /* See if this cell has already been cached ... */
+ unsigned int hash = hash_int (current_row, 0);
+ hash = hash_int (current_col, hash);
+ struct cache_datum *probe = NULL;
+ HMAP_FOR_EACH_WITH_HASH (probe, struct cache_datum, node, hash,
+ &gr->cache)
+ {
+ if (probe->row == current_row && probe->col == current_col)
+ break;
+ }
+ /* If not, then cache it. */
+ if (!probe)
+ {
+ char *str = CHAR_CAST (char *, xmlTextReaderValue (sd.xtr));
+ struct cache_datum *cell_data = XMALLOC (struct cache_datum);
+ cell_data->row = current_row;
+ cell_data->col = current_col;
+ cell_data->value = str;
+ hmap_insert (&gr->cache, &cell_data->node, hash);
+ }
+ }
+ }
+
+ while (xmlTextReaderRead (sd.xtr))
+ {
+ process_node (gr, &sd);
+ if (sd.state == STATE_CELL && sd.node_type == XML_READER_TYPE_TEXT)
+ {
+ if (sd.current_sheet == n)
+ {
+ if (row == sd.row && column == sd.col)
+ break;
+ }
+ }
+ }
+
+ char *cell_content = CHAR_CAST (char *, xmlTextReaderValue (sd.xtr));
+ xmlFreeTextReader (sd.xtr);
+ return cell_content;
+}