[PATCH 1/1] Start sanitizing gaschange event information

Linus Torvalds torvalds at linux-foundation.org
Sun Oct 12 04:21:06 PDT 2014


From: Linus Torvalds <torvalds at linux-foundation.org>
Date: Sun, 17 Aug 2014 12:26:21 -0600
Subject: [PATCH 1/1] Start sanitizing gaschange event information

Decode the gasmix data into a sane format when creating the event, and
add the (currently unused) ability to specify a gas change to a
particular cylinder rather than (or in addition to) the gasmix.

Signed-off-by: Linus Torvalds <torvalds at linux-foundation.org>
---

This is entirely independent of the uemis downloader patches, so sent as a 
separate series of a single patch. 

I've been carrying this along for a longish while now, and quite frankly, 
on its own it does very little indeed, but it cleans up how we handle gas 
change events internally, and it at least makes it *possible* to indicate 
which cylinder you are using even if you have multiple cylinders with the 
same gas, because now our gaschange has both gas mix information _and_ the 
cylinder index.

A cylinder index of "-1" means that we should take the index based purely 
on the gasmix, the way we traditionally do. But you could now set it to 
some other cylinder explicitly, even if we don't actually have the 
interfaces to do that yet.

So think of this as cleanup and preparation, but no new actual features.

 dive.c                          |  68 +++++++++++++++++---
 dive.h                          |  22 ++++++-
 load-git.c                      |  21 ++++++-
 parse-xml.c                     | 136 ++++++++++++++++++++++++----------------
 profile.c                       |  22 ++++---
 qt-ui/profile/diveeventitem.cpp |   8 +--
 save-git.c                      |  27 +++++---
 save-xml.c                      |  27 +++++---
 8 files changed, 234 insertions(+), 97 deletions(-)

diff --git a/dive.c b/dive.c
index 9972212aa5d4..e7bf685e65da 100644
--- a/dive.c
+++ b/dive.c
@@ -26,7 +26,31 @@ static const char *default_tags[] = {
 	QT_TRANSLATE_NOOP("gettextFromC", "deco")
 };
 
-void add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name)
+int event_is_gaschange(struct event *ev)
+{
+	return ev->type == SAMPLE_EVENT_GASCHANGE ||
+		ev->type == SAMPLE_EVENT_GASCHANGE2;
+}
+
+/*
+ * Does the gas mix data match the legacy
+ * libdivecomputer event format? If so,
+ * we can skip saving it, in order to maintain
+ * the old save formats. We'll re-generate the
+ * gas mix when loading.
+ */
+int event_gasmix_redundant(struct event *ev)
+{
+	int value = ev->value;
+	int o2, he;
+
+	o2 = (value & 0xffff) * 10;
+	he = (value >> 16) * 10;
+	return	o2 == ev->gas.mix.o2.permille &&
+		he == ev->gas.mix.he.permille;
+}
+
+struct event *add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name)
 {
 	struct event *ev, **p;
 	unsigned int size, len = strlen(name);
@@ -34,7 +58,7 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
 	size = sizeof(*ev) + len + 1;
 	ev = malloc(size);
 	if (!ev)
-		return;
+		return NULL;
 	memset(ev, 0, size);
 	memcpy(ev->name, name, len);
 	ev->time.seconds = time;
@@ -42,6 +66,22 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
 	ev->flags = flags;
 	ev->value = value;
 
+	/*
+	 * Expand the events into a sane format. Currently
+	 * just gas switches
+	 */
+	switch (type) {
+	case SAMPLE_EVENT_GASCHANGE2:
+		/* High 16 bits are He percentage */
+		ev->gas.mix.he.permille = (value >> 16) * 10;
+	/* Fallthrough */
+	case SAMPLE_EVENT_GASCHANGE:
+		/* Low 16 bits are O2 percentage */
+		ev->gas.mix.o2.permille = (value & 0xffff) * 10;
+		ev->gas.index = -1;
+		break;
+	}
+
 	p = &dc->events;
 
 	/* insert in the sorted list of events */
@@ -50,6 +90,7 @@ void add_event(struct divecomputer *dc, int time, int type, int flags, int value
 	ev->next = *p;
 	*p = ev;
 	remember_event(name);
+	return ev;
 }
 
 static int same_event(struct event *a, struct event *b)
@@ -107,14 +148,11 @@ void update_event_name(struct dive *d, struct event *event, char *name)
 /* this returns a pointer to static variable - so use it right away after calling */
 struct gasmix *get_gasmix_from_event(struct event *ev)
 {
-	static struct gasmix g;
-	g.o2.permille = g.he.permille = 0;
-	if (ev && (ev->type == SAMPLE_EVENT_GASCHANGE || ev->type == SAMPLE_EVENT_GASCHANGE2)) {
-		g.o2.permille = 10 * ev->value & 0xffff;
-		if (ev->type == SAMPLE_EVENT_GASCHANGE2)
-			g.he.permille = 10 * (ev->value >> 16);
-	}
-	return &g;
+	static struct gasmix dummy;
+	if (ev && event_is_gaschange(ev))
+		return &ev->gas.mix;
+
+	return &dummy;
 }
 
 int get_pressure_units(int mb, const char **units)
@@ -1543,6 +1581,7 @@ static void add_initial_gaschange(struct dive *dive, struct divecomputer *dc)
 static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int mapping[])
 {
 	int i;
+	struct event *ev;
 
 	/* Did the first gas get remapped? Add gas switch event */
 	if (mapping[0] > 0)
@@ -1559,6 +1598,15 @@ static void dc_cylinder_renumber(struct dive *dive, struct divecomputer *dc, int
 		if (sensor >= 0)
 			s->sensor = sensor;
 	}
+
+	/* Remap the gas change indexes */
+	for (ev = dc->events; ev; ev = ev->next) {
+		if (!event_is_gaschange(ev))
+			continue;
+		if (ev->gas.index < 0)
+			continue;
+		ev->gas.index = mapping[ev->gas.index];
+	}
 }
 
 /*
diff --git a/dive.h b/dive.h
index eb890911857b..55396d8fe77d 100644
--- a/dive.h
+++ b/dive.h
@@ -85,11 +85,29 @@ typedef struct
 struct event {
 	struct event *next;
 	duration_t time;
-	int type, flags, value;
+	int type;
+	/* This is the annoying libdivecomputer format. */
+	int flags, value;
+	/* .. and this is our "extended" data for some event types */
+	union {
+		/*
+		 * Currently only for gas switch events.
+		 *
+		 * NOTE! The index may be -1, which means "unknown". In that
+		 * case, the get_cylinder_index() function will give the best
+		 * match with the cylinders in the dive based on gasmix.
+		 */
+		struct {
+			int index;
+			struct gasmix mix;
+		} gas;
+	};
 	bool deleted;
 	char name[];
 };
 
+extern int event_is_gaschange(struct event *ev);
+extern int event_gasmix_redundant(struct event *ev);
 
 extern int get_pressure_units(int mb, const char **units);
 extern double get_depth_units(int mm, int *frac, const char **units);
@@ -651,7 +669,7 @@ extern void copy_samples(struct divecomputer *s, struct divecomputer *d);
 extern bool is_cylinder_used(struct dive *dive, int idx);
 extern void fill_default_cylinder(cylinder_t *cyl);
 extern void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int time, int idx);
-extern void add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name);
+extern struct event *add_event(struct divecomputer *dc, int time, int type, int flags, int value, const char *name);
 extern void remove_event(struct event *event);
 extern void update_event_name(struct dive *d, struct event* event, char *name);
 extern void per_cylinder_mean_depth(struct dive *dive, struct divecomputer *dc, int *mean, int *duration);
diff --git a/load-git.c b/load-git.c
index e61a0ca13a95..16aa1958fcec 100644
--- a/load-git.c
+++ b/load-git.c
@@ -529,6 +529,13 @@ static void parse_event_keyvalue(void *_event, const char *key, const char *valu
 		event->value = val;
 	} else if (!strcmp(key, "name")) {
 		/* We get the name from the string handling */
+	} else if (!strcmp(key, "cylinder")) {
+		/* NOTE! We add one here as a marker that "yes, we got a cylinder index" */
+		event->gas.index = 1+get_index(value);
+	} else if (!strcmp(key, "o2")) {
+		event->gas.mix.o2 = get_fraction(value);
+	} else if (!strcmp(key, "he")) {
+		event->gas.mix.he = get_fraction(value);
 	} else
 		report_error("Unexpected event key/value pair (%s/%s)", key, value);
 }
@@ -538,7 +545,7 @@ static void parse_dc_event(char *line, struct membuffer *str, void *_dc)
 	int m, s = 0;
 	const char *name;
 	struct divecomputer *dc = _dc;
-	struct event event = { 0 };
+	struct event event = { 0 }, *ev;
 
 	m = strtol(line, &line, 10);
 	if (*line == ':')
@@ -557,7 +564,17 @@ static void parse_dc_event(char *line, struct membuffer *str, void *_dc)
 	name = "";
 	if (str->len)
 		name = mb_cstring(str);
-	add_event(dc, event.time.seconds, event.type, event.flags, event.value, name);
+	ev = add_event(dc, event.time.seconds, event.type, event.flags, event.value, name);
+	if (ev && event_is_gaschange(ev)) {
+		/*
+		 * We subtract one here because "0" is "no index",
+		 * and the parsing will add one for actual cylinder
+		 * index data (see parse_event_keyvalue)
+		 */
+		ev->gas.index = event.gas.index-1;
+		if (event.gas.mix.o2.permille || event.gas.mix.he.permille)
+			ev->gas.mix = event.gas.mix;
+	}
 }
 
 static void parse_trip_date(char *line, struct membuffer *str, void *_trip)
diff --git a/parse-xml.c b/parse-xml.c
index 6e74e8aaf449..e88c9b944496 100644
--- a/parse-xml.c
+++ b/parse-xml.c
@@ -95,17 +95,17 @@ const struct units IMPERIAL_units = IMPERIAL_UNITS;
 /*
  * Dive info as it is being built up..
  */
+#define MAX_EVENT_NAME 128
 static struct divecomputer *cur_dc;
 static struct dive *cur_dive;
 static dive_trip_t *cur_trip = NULL;
 static struct sample *cur_sample;
 static struct picture *cur_picture;
-static struct {
-	int active;
-	duration_t time;
-	int type, flags, value;
-	const char *name;
-} cur_event;
+static union {
+	struct event event;
+	char allocation[sizeof(struct event)+MAX_EVENT_NAME];
+} event_allocation = { .event.deleted = 1 };
+#define cur_event event_allocation.event
 static struct {
 	struct {
 		const char *model;
@@ -527,6 +527,15 @@ static void utf8_string(char *buffer, void *_res)
 		*res = strdup(buffer);
 }
 
+static void event_name(char *buffer, char *name)
+{
+	int size = trimspace(buffer);
+	if (size >= MAX_EVENT_NAME)
+		size = MAX_EVENT_NAME-1;
+	memcpy(name, buffer, size);
+	name[size] = 0;
+}
+
 /* Extract the dive computer type from the xml text buffer */
 static void get_dc_type(char *buffer, enum dive_comp_type *i)
 {
@@ -704,16 +713,22 @@ static void try_to_match_autogroup(const char *name, char *buf)
 
 void add_gas_switch_event(struct dive *dive, struct divecomputer *dc, int seconds, int idx)
 {
-	/* The gas switch event format is insane. It will be fixed, I think */
-	int o2 = get_o2(&dive->cylinder[idx].gasmix);
-	int he = get_he(&dive->cylinder[idx].gasmix);
+	/* The gas switch event format is insane for historical reasons */
+	struct gasmix *mix = &dive->cylinder[idx].gasmix;
+	int o2 = get_o2(mix);
+	int he = get_he(mix);
+	struct event *ev;
 	int value;
 
 	o2 = (o2 + 5) / 10;
 	he = (he + 5) / 10;
 	value = o2 + (he << 16);
 
-	add_event(dc, seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
+	ev = add_event(dc, seconds, he ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE, 0, value, "gaschange");
+	if (ev) {
+		ev->gas.index = idx;
+		ev->gas.mix = *mix;
+	}
 }
 
 static void get_cylinderindex(char *buffer, uint8_t *i)
@@ -751,9 +766,9 @@ static void try_to_fill_dc_settings(const char *name, char *buf)
 static void try_to_fill_event(const char *name, char *buf)
 {
 	start_match("event", name, buf);
-	if (MATCH("event", utf8_string, &cur_event.name))
+	if (MATCH("event", event_name, cur_event.name))
 		return;
-	if (MATCH("name", utf8_string, &cur_event.name))
+	if (MATCH("name", event_name, cur_event.name))
 		return;
 	if (MATCH("time", eventtime, &cur_event.time))
 		return;
@@ -763,6 +778,15 @@ static void try_to_fill_event(const char *name, char *buf)
 		return;
 	if (MATCH("value", get_index, &cur_event.value))
 		return;
+	if (MATCH("cylinder", get_index, &cur_event.gas.index)) {
+		/* We add one to indicate that we got an actual cylinder index value */
+		cur_event.gas.index++;
+		return;
+	}
+	if (MATCH("o2", percent, &cur_event.gas.mix.o2))
+		return;
+	if (MATCH("he", percent, &cur_event.gas.mix.he))
+		return;
 	nonmatch("event", name, buf);
 }
 
@@ -1332,36 +1356,38 @@ static void trip_end(void)
 static void event_start(void)
 {
 	memset(&cur_event, 0, sizeof(cur_event));
-	cur_event.active = 1;
+	cur_event.deleted = 0;	/* Active */
 }
 
 static void event_end(void)
 {
 	struct divecomputer *dc = get_dc();
-	if (cur_event.name) {
-		if (strcmp(cur_event.name, "surface") != 0) {
-			/* 123 is a magic event that we used for a while to encode images in dives */
-			if (cur_event.type == 123) {
-				struct picture *pic = alloc_picture();
-				pic->filename = strdup(cur_event.name);
-				/* theoretically this could fail - but we didn't support multi year offsets */
-				pic->offset.seconds = cur_event.time.seconds;
-				dive_add_picture(cur_dive, pic);
-			} else {
-				/* At some point gas change events did not have any type. Thus we need to add
-				 * one on import, if we encounter the type one missing.
-				 */
-				if (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
-					cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
-
-				add_event(dc, cur_event.time.seconds,
-					  cur_event.type, cur_event.flags,
-					  cur_event.value, cur_event.name);
+	if (strcmp(cur_event.name, "surface") != 0) {			/* 123 is a magic event that we used for a while to encode images in dives */
+		if (cur_event.type == 123) {
+			struct picture *pic = alloc_picture();
+			pic->filename = strdup(cur_event.name);
+			/* theoretically this could fail - but we didn't support multi year offsets */
+			pic->offset.seconds = cur_event.time.seconds;
+			dive_add_picture(cur_dive, pic);
+		} else {
+			struct event *ev;
+			/* At some point gas change events did not have any type. Thus we need to add
+			 * one on import, if we encounter the type one missing.
+			 */
+			if (cur_event.type == 0 && strcmp(cur_event.name, "gaschange") == 0)
+				cur_event.type = cur_event.value >> 16 > 0 ? SAMPLE_EVENT_GASCHANGE2 : SAMPLE_EVENT_GASCHANGE;
+			ev = add_event(dc, cur_event.time.seconds,
+				       cur_event.type, cur_event.flags,
+				       cur_event.value, cur_event.name);
+			if (ev && event_is_gaschange(ev)) {
+				/* See try_to_fill_event() on why the filled-in index is one too big */
+				ev->gas.index = cur_event.gas.index-1;
+				if (cur_event.gas.mix.o2.permille || cur_event.gas.mix.he.permille)
+					ev->gas.mix = cur_event.gas.mix;
 			}
 		}
-		free((void *)cur_event.name);
 	}
-	cur_event.active = 0;
+	cur_event.deleted = 1;	/* No longer active */
 }
 
 static void picture_start(void)
@@ -1472,7 +1498,7 @@ static void entry(const char *name, char *buf)
 		try_to_match_autogroup(name, buf);
 		return;
 	}
-	if (cur_event.active) {
+	if (!cur_event.deleted) {
 		try_to_fill_event(name, buf);
 		return;
 	}
@@ -1722,71 +1748,71 @@ extern int dm4_events(void *handle, int columns, char **data, char **column)
 		switch (atoi(data[2])) {
 		case 1:
 			/* 1 Mandatory Safety Stop */
-			cur_event.name = strdup("safety stop (mandatory)");
+			strcpy(cur_event.name, "safety stop (mandatory)");
 			break;
 		case 3:
 			/* 3 Deco */
 			/* What is Subsurface's term for going to
 				 * deco? */
-			cur_event.name = strdup("deco");
+			strcpy(cur_event.name, "deco");
 			break;
 		case 4:
 			/* 4 Ascent warning */
-			cur_event.name = strdup("ascent");
+			strcpy(cur_event.name, "ascent");
 			break;
 		case 5:
 			/* 5 Ceiling broken */
-			cur_event.name = strdup("violation");
+			strcpy(cur_event.name, "violation");
 			break;
 		case 6:
 			/* 6 Mandatory safety stop ceiling error */
-			cur_event.name = strdup("violation");
+			strcpy(cur_event.name, "violation");
 			break;
 		case 7:
 			/* 7 Below deco floor */
-			cur_event.name = strdup("below floor");
+			strcpy(cur_event.name, "below floor");
 			break;
 		case 8:
 			/* 8 Dive time alarm */
-			cur_event.name = strdup("divetime");
+			strcpy(cur_event.name, "divetime");
 			break;
 		case 9:
 			/* 9 Depth alarm */
-			cur_event.name = strdup("maxdepth");
+			strcpy(cur_event.name, "maxdepth");
 			break;
 		case 10:
 		/* 10 OLF 80% */
 		case 11:
 			/* 11 OLF 100% */
-			cur_event.name = strdup("OLF");
+			strcpy(cur_event.name, "OLF");
 			break;
 		case 12:
 			/* 12 High pO₂ */
-			cur_event.name = strdup("PO2");
+			strcpy(cur_event.name, "PO2");
 			break;
 		case 13:
 			/* 13 Air time */
-			cur_event.name = strdup("airtime");
+			strcpy(cur_event.name, "airtime");
 			break;
 		case 17:
 			/* 17 Ascent warning */
-			cur_event.name = strdup("ascent");
+			strcpy(cur_event.name, "ascent");
 			break;
 		case 18:
 			/* 18 Ceiling error */
-			cur_event.name = strdup("ceiling");
+			strcpy(cur_event.name, "ceiling");
 			break;
 		case 19:
 			/* 19 Surfaced */
-			cur_event.name = strdup("surface");
+			strcpy(cur_event.name, "surface");
 			break;
 		case 20:
 			/* 20 Deco */
-			cur_event.name = strdup("deco");
+			strcpy(cur_event.name, "deco");
 			break;
 		case 22:
 			/* 22 Mandatory safety stop violation */
-			cur_event.name = strdup("violation");
+			strcpy(cur_event.name, "violation");
 			break;
 		case 257:
 			/* 257 Dive active */
@@ -1796,14 +1822,14 @@ extern int dm4_events(void *handle, int columns, char **data, char **column)
 		case 258:
 			/* 258 Bookmark */
 			if (data[3]) {
-				cur_event.name = strdup("heading");
+				strcpy(cur_event.name, "heading");
 				cur_event.value = atoi(data[3]);
 			} else {
-				cur_event.name = strdup("bookmark");
+				strcpy(cur_event.name, "bookmark");
 			}
 			break;
 		default:
-			cur_event.name = strdup("unknown");
+			strcpy(cur_event.name, "unknown");
 			cur_event.value = atoi(data[2]);
 			break;
 		}
@@ -1986,7 +2012,7 @@ extern int shearwater_changes(void *handle, int columns, char **data, char **col
 	if (data[0])
 		cur_event.time.seconds = atoi(data[0]);
 	if (data[1]) {
-		cur_event.name = strdup("gaschange");
+		strcpy(cur_event.name, "gaschange");
 		cur_event.value = atof(data[1]) * 100;
 	}
 	event_end();
diff --git a/profile.c b/profile.c
index 71a9164e6190..79917d6e6d4a 100644
--- a/profile.c
+++ b/profile.c
@@ -289,6 +289,14 @@ struct plot_info *analyze_plot_info(struct plot_info *pi)
 	return pi;
 }
 
+/*
+ * If the event has an explicit cylinder index,
+ * we return that. If it doesn't, we return the best
+ * match based on the gasmix.
+ *
+ * Some dive computers give cylinder indexes, some
+ * give just the gas mix.
+ */
 int get_cylinder_index(struct dive *dive, struct event *ev)
 {
 	int i;
@@ -296,10 +304,9 @@ int get_cylinder_index(struct dive *dive, struct event *ev)
 	int target_o2, target_he;
 	struct gasmix *g;
 
-	/*
-	 * Crazy gas change events give us odd encoded o2/he in percent.
-	 * Decode into our internal permille format.
-	 */
+	if (ev->gas.index >= 0)
+		return ev->gas.index;
+
 	g = get_gasmix_from_event(ev);
 	target_o2 = get_o2(g);
 	target_he = get_he(g);
@@ -318,13 +325,8 @@ int get_cylinder_index(struct dive *dive, struct event *ev)
 		delta_o2 = get_o2(&cyl->gasmix) - target_o2;
 		delta_he = get_he(&cyl->gasmix) - target_he;
 		distance = delta_o2 * delta_o2;
+		distance += delta_he * delta_he;
 
-		/* Check the event type to figure out if we should care about the he part.
-		 * SAMPLE_EVENT_GASCHANGE, aka without he
-		 * SAMPLE_EVENT_GASCHANGE2, aka with he
-		 */
-		if (ev->type == SAMPLE_EVENT_GASCHANGE2)
-			distance += delta_he * delta_he;
 		if (distance >= score)
 			continue;
 		score = distance;
diff --git a/qt-ui/profile/diveeventitem.cpp b/qt-ui/profile/diveeventitem.cpp
index b9bb4482b843..c1ea4854485f 100644
--- a/qt-ui/profile/diveeventitem.cpp
+++ b/qt-ui/profile/diveeventitem.cpp
@@ -66,10 +66,10 @@ void DiveEventItem::setupPixmap()
 		setPixmap(EVENT_PIXMAP(":flag"));
 	} else if (strcmp(internalEvent->name, "heading") == 0) {
 		setPixmap(EVENT_PIXMAP(":flag"));
-	} else if (internalEvent->type == SAMPLE_EVENT_GASCHANGE || internalEvent->type == SAMPLE_EVENT_GASCHANGE2) {
-		if (internalEvent->value >> 16)
+	} else if (event_is_gaschange(internalEvent)) {
+		if (internalEvent->gas.mix.he.permille)
 			setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeTrimix"));
-		else if (internalEvent->value == 0)
+		else if (gasmix_is_air(&internalEvent->gas.mix))
 			setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeAir"));
 		else
 			setPixmap(EVENT_PIXMAP_BIGGER(":gaschangeNitrox"));
@@ -86,7 +86,7 @@ void DiveEventItem::setupToolTipString()
 	int value = internalEvent->value;
 	int type = internalEvent->type;
 	if (value) {
-		if (type == SAMPLE_EVENT_GASCHANGE || type == SAMPLE_EVENT_GASCHANGE2) {
+		if (event_is_gaschange(internalEvent)) {
 			QModelIndexList result = dataModel->match(dataModel->index(0, DivePlotDataModel::TIME), Qt::DisplayRole, internalEvent->time.seconds);
 			if (result.isEmpty()) {
 				Q_ASSERT("can't find a spot in the dataModel");
diff --git a/save-git.c b/save-git.c
index a1ef022fb2e9..308a02c831e8 100644
--- a/save-git.c
+++ b/save-git.c
@@ -119,6 +119,18 @@ static void save_tags(struct membuffer *b, struct tag_entry *tags)
 	put_string(b, "\n");
 }
 
+static void put_gasmix(struct membuffer *b, struct gasmix *mix)
+{
+	int o2 = mix->o2.permille;
+	int he = mix->he.permille;
+
+	if (o2) {
+		put_format(b, " o2=%u.%u%%", FRACTION(o2, 10));
+		if (he)
+			put_format(b, " he=%u.%u%%", FRACTION(he, 10));
+	}
+}
+
 static void save_cylinder_info(struct membuffer *b, struct dive *dive)
 {
 	int i, nr;
@@ -128,8 +140,6 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
 		cylinder_t *cylinder = dive->cylinder + i;
 		int volume = cylinder->type.size.mliter;
 		const char *description = cylinder->type.description;
-		int o2 = cylinder->gasmix.o2.permille;
-		int he = cylinder->gasmix.he.permille;
 
 		put_string(b, "cylinder");
 		if (volume)
@@ -137,11 +147,7 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
 		put_pressure(b, cylinder->type.workingpressure, " workpressure=", "bar");
 		show_utf8(b, " description=", description, "");
 		strip_mb(b);
-		if (o2) {
-			put_format(b, " o2=%u.%u%%", FRACTION(o2, 10));
-			if (he)
-				put_format(b, " he=%u.%u%%", FRACTION(he, 10));
-		}
+		put_gasmix(b, &cylinder->gasmix);
 		put_pressure(b, cylinder->start, " start=", "bar");
 		put_pressure(b, cylinder->end, " end=", "bar");
 		put_string(b, "\n");
@@ -292,6 +298,13 @@ static void save_one_event(struct membuffer *b, struct event *ev)
 	show_index(b, ev->flags, "flags=", "");
 	show_index(b, ev->value, "value=", "");
 	show_utf8(b, " name=", ev->name, "");
+	if (event_is_gaschange(ev)) {
+		if (ev->gas.index >= 0) {
+			show_index(b, ev->gas.index, "cylinder=", "");
+			put_gasmix(b, &ev->gas.mix);
+		} else if (!event_gasmix_redundant(ev))
+			put_gasmix(b, &ev->gas.mix);
+	}
 	put_string(b, "\n");
 }
 
diff --git a/save-xml.c b/save-xml.c
index fe804cb6d73b..e9f769d3f885 100644
--- a/save-xml.c
+++ b/save-xml.c
@@ -148,6 +148,18 @@ static void save_overview(struct membuffer *b, struct dive *dive)
 	show_utf8(b, dive->suit, "  <suit>", "</suit>\n", 0);
 }
 
+static void put_gasmix(struct membuffer *b, struct gasmix *mix)
+{
+	int o2 = mix->o2.permille;
+	int he = mix->he.permille;
+
+	if (o2) {
+		put_format(b, " o2='%u.%u%%'", FRACTION(o2, 10));
+		if (he)
+			put_format(b, " he='%u.%u%%'", FRACTION(he, 10));
+	}
+}
+
 static void save_cylinder_info(struct membuffer *b, struct dive *dive)
 {
 	int i, nr;
@@ -158,19 +170,13 @@ static void save_cylinder_info(struct membuffer *b, struct dive *dive)
 		cylinder_t *cylinder = dive->cylinder + i;
 		int volume = cylinder->type.size.mliter;
 		const char *description = cylinder->type.description;
-		int o2 = cylinder->gasmix.o2.permille;
-		int he = cylinder->gasmix.he.permille;
 
 		put_format(b, "  <cylinder");
 		if (volume)
 			put_milli(b, " size='", volume, " l'");
 		put_pressure(b, cylinder->type.workingpressure, " workpressure='", " bar'");
 		show_utf8(b, description, " description='", "'", 1);
-		if (o2) {
-			put_format(b, " o2='%u.%u%%'", FRACTION(o2, 10));
-			if (he)
-				put_format(b, " he='%u.%u%%'", FRACTION(he, 10));
-		}
+		put_gasmix(b, &cylinder->gasmix);
 		put_pressure(b, cylinder->start, " start='", " bar'");
 		put_pressure(b, cylinder->end, " end='", " bar'");
 		put_format(b, " />\n");
@@ -261,6 +267,13 @@ static void save_one_event(struct membuffer *b, struct event *ev)
 	show_index(b, ev->flags, "flags='", "'");
 	show_index(b, ev->value, "value='", "'");
 	show_utf8(b, ev->name, " name='", "'", 1);
+	if (event_is_gaschange(ev)) {
+		if (ev->gas.index >= 0) {
+			show_index(b, ev->gas.index, "cylinder='", "'");
+			put_gasmix(b, &ev->gas.mix);
+		} else if (!event_gasmix_redundant(ev))
+			put_gasmix(b, &ev->gas.mix);
+	}
 	put_format(b, " />\n");
 }
 
-- 
2.1.2.336.g325602c



More information about the subsurface mailing list