diff --git a/examples/output_xml.c b/examples/output_xml.c
index 9a5a9a8..58e045e 100644
--- a/examples/output_xml.c
+++ b/examples/output_xml.c
@@ -414,6 +414,24 @@ dctool_xml_output_write (dctool_output_t *abstract, dc_parser_t *parser, const u
convert_pressure(atmospheric, output->units));
}
+ message ("Parsing strings.\n");
+ int idx;
+ for (idx = 0; idx < 100; idx++) {
+ dc_field_string_t str = { NULL };
+ status = dc_parser_get_field(parser, DC_FIELD_STRING, idx, &str);
+ if (status != DC_STATUS_SUCCESS && status != DC_STATUS_UNSUPPORTED) {
+ ERROR ("Error parsing strings");
+ goto cleanup;
+ }
+ if (status == DC_STATUS_UNSUPPORTED)
+ break;
+ if (!str.desc || !str.value)
+ break;
+ fprintf (output->ostream, "\n",
+ str.desc, str.value);
+
+ }
+
// Parse the sample data.
message ("Parsing the sample data.\n");
status = dc_parser_samples_foreach (parser, sample_cb, &sampledata);
diff --git a/include/libdivecomputer/parser.h b/include/libdivecomputer/parser.h
index d12ca26..6868883 100644
--- a/include/libdivecomputer/parser.h
+++ b/include/libdivecomputer/parser.h
@@ -62,9 +62,13 @@ typedef enum dc_field_type_t {
DC_FIELD_TEMPERATURE_MAXIMUM,
DC_FIELD_TANK_COUNT,
DC_FIELD_TANK,
- DC_FIELD_DIVEMODE
+ DC_FIELD_DIVEMODE,
+ DC_FIELD_STRING,
} dc_field_type_t;
+// Make it easy to test support compile-time with "#ifdef DC_FIELD_STRING"
+#define DC_FIELD_STRING DC_FIELD_STRING
+
typedef enum parser_sample_event_t {
SAMPLE_EVENT_NONE,
SAMPLE_EVENT_DECOSTOP,
@@ -191,6 +195,11 @@ typedef struct dc_tank_t {
double endpressure; /* End pressure (bar) */
} dc_tank_t;
+typedef struct dc_field_string_t {
+ const char *desc;
+ const char *value;
+} dc_field_string_t;
+
typedef union dc_sample_value_t {
unsigned int time;
double depth;
diff --git a/msvc/libdivecomputer.vcproj b/msvc/libdivecomputer.vcproj
index 507140e..047e35b 100644
--- a/msvc/libdivecomputer.vcproj
+++ b/msvc/libdivecomputer.vcproj
@@ -498,6 +498,10 @@
RelativePath="..\src\tecdiving_divecomputereu_parser.c"
>
+
+
diff --git a/src/Makefile.am b/src/Makefile.am
index 4d19b48..b21ef3b 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -69,6 +69,7 @@ libdivecomputer_la_SOURCES = \
rbstream.h rbstream.c \
checksum.h checksum.c \
array.h array.c \
+ field-cache.h field-cache.c \
buffer.c \
cochran_commander.h cochran_commander.c cochran_commander_parser.c \
tecdiving_divecomputereu.h tecdiving_divecomputereu.c tecdiving_divecomputereu_parser.c \
diff --git a/src/field-cache.c b/src/field-cache.c
new file mode 100644
index 0000000..890320a
--- /dev/null
+++ b/src/field-cache.c
@@ -0,0 +1,64 @@
+#include
+#include
+#include
+
+#include "parser-private.h"
+#include "field-cache.h"
+
+/*
+ * The field cache 'string' interface has some simple rules:
+ * the "descriptor" part is assumed to be a static allocation,
+ * while the "value" is something that this interface will
+ * alway sallocate with 'strdup()', so you can generate it
+ * dynamically on the stack or whatever without having to
+ * worry about it.
+ */
+dc_status_t dc_field_add_string(dc_field_cache_t *cache, const char *desc, const char *value)
+{
+ int i;
+
+ cache->initialized |= 1 << DC_FIELD_STRING;
+ for (i = 0; i < MAXSTRINGS; i++) {
+ dc_field_string_t *str = cache->strings+i;
+ if (str->desc)
+ continue;
+ str->value = strdup(value);
+ if (!str->value)
+ return DC_STATUS_NOMEMORY;
+ str->desc = desc;
+ return DC_STATUS_SUCCESS;
+ }
+ return DC_STATUS_INVALIDARGS;
+}
+
+dc_status_t dc_field_add_string_fmt(dc_field_cache_t *cache, const char *desc, const char *fmt, ...)
+{
+ char buffer[256];
+ va_list ap;
+
+ /*
+ * We ignore the return value from vsnprintf, and we
+ * always NUL-terminate the destination buffer ourselves.
+ *
+ * That way we don't have to worry about random bad legacy
+ * implementations.
+ */
+ va_start(ap, fmt);
+ buffer[sizeof(buffer)-1] = 0;
+ (void) vsnprintf(buffer, sizeof(buffer)-1, fmt, ap);
+ va_end(ap);
+
+ return dc_field_add_string(cache, desc, buffer);
+}
+
+dc_status_t dc_field_get_string(dc_field_cache_t *cache, unsigned idx, dc_field_string_t *value)
+{
+ if (idx < MAXSTRINGS) {
+ dc_field_string_t *res = cache->strings+idx;
+ if (res->desc && res->value) {
+ *value = *res;
+ return DC_STATUS_SUCCESS;
+ }
+ }
+ return DC_STATUS_UNSUPPORTED;
+}
diff --git a/src/field-cache.h b/src/field-cache.h
new file mode 100644
index 0000000..ec8a7f4
--- /dev/null
+++ b/src/field-cache.h
@@ -0,0 +1,63 @@
+#include
+
+#define MAXGASES 16
+#define MAXSTRINGS 32
+
+// dc_get_field() data
+typedef struct dc_field_cache {
+ unsigned int initialized;
+
+ // DC_GET_FIELD_xyz
+ unsigned int DIVETIME;
+ double MAXDEPTH;
+ double AVGDEPTH;
+ double ATMOSPHERIC;
+ dc_divemode_t DIVEMODE;
+ unsigned int GASMIX_COUNT;
+ dc_salinity_t SALINITY;
+ dc_gasmix_t GASMIX[MAXGASES];
+
+ // misc - clean me up!
+ double lowsetpoint;
+ double highsetpoint;
+ double customsetpoint;
+
+ // This (slong with GASMIX) should be something like
+ // dc_tank_t TANK[MAXGASES]
+ // but that's for later
+ dc_tankinfo_t tankinfo[MAXGASES];
+ double tanksize[MAXGASES];
+ double tankworkingpressure[MAXGASES];
+
+ // DC_GET_FIELD_STRING
+ dc_field_string_t strings[MAXSTRINGS];
+} dc_field_cache_t;
+
+dc_status_t dc_field_add_string(dc_field_cache_t *, const char *desc, const char *data);
+dc_status_t dc_field_add_string_fmt(dc_field_cache_t *, const char *desc, const char *fmt, ...);
+dc_status_t dc_field_get_string(dc_field_cache_t *, unsigned idx, dc_field_string_t *value);
+
+/*
+ * Macro to make it easy to set DC_FIELD_xyz values.
+ *
+ * This explains why dc_field_cache member names are
+ * those odd all-capitalized names: they match the
+ * names of the DC_FIELD_xyz enums.
+ */
+#define DC_ASSIGN_FIELD(cache, name, value) do { \
+ (cache).initialized |= 1u << DC_FIELD_##name; \
+ (cache).name = (value); \
+} while (0)
+
+#define DC_ASSIGN_IDX(cache, name, idx, value) do { \
+ (cache).initialized |= 1u << DC_FIELD_##name; \
+ (cache).name[idx] = (value); \
+} while (0)
+
+// Ugly define thing makes the code much easier to read
+// I'd love to use __typeof__, but that's a gcc'ism
+#define DC_FIELD_VALUE(cache, p, NAME) \
+ (memcpy((p), &(cache).NAME, sizeof((cache).NAME)), DC_STATUS_SUCCESS)
+
+#define DC_FIELD_INDEX(cache, p, NAME, idx) \
+ (memcpy((p), (cache).NAME+idx, sizeof((cache).NAME[0])), DC_STATUS_SUCCESS)