+static char *
+ods_get_sheet_cell (struct spreadsheet *s, int n, int row, int column)
+{
+ struct ods_reader *r = (struct ods_reader *) s;
+ struct state_data sd;
+
+ /* 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 (n, 0);
+ hash = hash_int (row, hash);
+ hash = hash_int (column, hash);
+
+ HMAP_FOR_EACH_WITH_HASH (lookup, struct cache_datum, node, hash,
+ &r->cache)
+ {
+ if (lookup->row == row && lookup->col == column
+ && lookup->sheet == n)
+ {
+ break;
+ }
+ }
+ if (lookup)
+ {
+ return lookup->value ? strdup (lookup->value) : NULL;
+ }
+ }
+
+ state_data_init (r, &sd);
+
+ char *cell_content = NULL;
+
+ int prev_col = 0;
+ int prev_row = 0;
+ while (1 == xmlTextReaderRead (sd.xtr))
+ {
+ process_node (r, &sd);
+ if (sd.row > prev_row)
+ prev_col = 0;
+
+ if (sd.state == STATE_CELL_CONTENT
+ && sd.current_sheet == n
+ && sd.node_type == XML_READER_TYPE_TEXT)
+ {
+ /* When cell contents are encountered, copy and save it, discarding
+ any older content. */
+ free (cell_content);
+ cell_content = CHAR_CAST (char *, xmlTextReaderValue (sd.xtr));
+ }
+ if (sd.state == STATE_ROW
+ && sd.current_sheet == n
+ && sd.node_type == XML_READER_TYPE_ELEMENT)
+ {
+ /* At the start of a row, free the cell contents and set it to NULL. */
+ free (cell_content);
+ cell_content = NULL;
+ }
+ if (sd.state == STATE_ROW
+ && sd.current_sheet == n
+ &&
+ (sd.node_type == XML_READER_TYPE_END_ELEMENT
+ ||
+ xmlTextReaderIsEmptyElement (sd.xtr)))
+ {
+ if (use_cache)
+ {
+ for (int c = prev_col; c < sd.col; ++c)
+ {
+ /* See if this cell has already been cached ... */
+ unsigned int hash = hash_int (sd.current_sheet, 0);
+ hash = hash_int (sd.row - 1, hash);
+ hash = hash_int (c, hash);
+ struct cache_datum *probe = NULL;
+ struct cache_datum *next;
+ HMAP_FOR_EACH_WITH_HASH_SAFE (probe, next, struct cache_datum, node, hash,
+ &r->cache)
+ {
+ if (probe->row == sd.row - 1 && probe->col == c
+ && probe->sheet == sd.current_sheet)
+ break;
+ probe = NULL;
+ }
+ /* If not, then cache it. */
+ if (!probe)
+ {
+ struct cache_datum *cell_data = XMALLOC (struct cache_datum);
+ cell_data->row = sd.row - 1;
+ cell_data->col = c;
+ cell_data->sheet = sd.current_sheet;
+ cell_data->value = cell_content ? strdup (cell_content) : NULL;
+
+ hmap_insert (&r->cache, &cell_data->node, hash);
+ }
+ }
+ }
+
+ if (sd.row == row + 1 && sd.col >= column + 1)
+ {
+ break;
+ }
+
+ prev_col = sd.col;
+ prev_row = sd.row;
+ }
+ }
+
+ state_data_destroy (&sd);
+ return cell_content;
+}