| 
									
										
										
										
											2017-04-27 20:18:03 +02:00
										 |  |  | // SPDX-License-Identifier: GPL-2.0
 | 
					
						
							| 
									
										
										
										
											2017-03-11 22:08:31 +02:00
										 |  |  | #ifdef __clang__
 | 
					
						
							| 
									
										
										
										
											2016-03-09 15:18:34 -03:00
										 |  |  | // Clang has a bug on zero-initialization of C structs.
 | 
					
						
							|  |  |  | #pragma clang diagnostic ignored "-Wmissing-field-initializers"
 | 
					
						
							| 
									
										
										
										
											2017-03-11 22:08:31 +02:00
										 |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2016-03-09 15:18:34 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | #include <stdarg.h>
 | 
					
						
							|  |  |  | #include <stdlib.h>
 | 
					
						
							|  |  |  | #include <string.h>
 | 
					
						
							|  |  |  | #include <stdio.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "dive.h"
 | 
					
						
							|  |  |  | #include "membuffer.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-24 12:47:26 +12:00
										 |  |  | char *detach_buffer(struct membuffer *b) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2015-01-24 12:47:26 +12:00
										 |  |  | 	char *result = b->buffer; | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | 	b->buffer = NULL; | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 	b->len = 0; | 
					
						
							|  |  |  | 	b->alloc = 0; | 
					
						
							| 
									
										
										
										
											2015-01-24 12:47:26 +12:00
										 |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void free_buffer(struct membuffer *b) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	free(detach_buffer(b)); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void flush_buffer(struct membuffer *b, FILE *f) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 	if (b->len) { | 
					
						
							|  |  |  | 		fwrite(b->buffer, 1, b->len, f); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | 		free_buffer(b); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | void strip_mb(struct membuffer *b) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-27 20:09:57 -08:00
										 |  |  | 	while (b->len && isspace(b->buffer[b->len - 1])) | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 		b->len--; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Running out of memory isn't really an issue these days. | 
					
						
							|  |  |  |  * So rather than do insane error handling and making the | 
					
						
							|  |  |  |  * interface very complex, we'll just die. It won't happen | 
					
						
							|  |  |  |  * unless you're running on a potato. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void oom(void) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	fprintf(stderr, "Out of memory\n"); | 
					
						
							|  |  |  | 	exit(1); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void make_room(struct membuffer *b, unsigned int size) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 	unsigned int needed = b->len + size; | 
					
						
							|  |  |  | 	if (needed > b->alloc) { | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | 		char *n; | 
					
						
							|  |  |  | 		/* round it up to not reallocate all the time.. */ | 
					
						
							|  |  |  | 		needed = needed * 9 / 8 + 1024; | 
					
						
							|  |  |  | 		n = realloc(b->buffer, needed); | 
					
						
							|  |  |  | 		if (!n) | 
					
						
							|  |  |  | 			oom(); | 
					
						
							|  |  |  | 		b->buffer = n; | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 		b->alloc = needed; | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 09:46:17 -08:00
										 |  |  | const char *mb_cstring(struct membuffer *b) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	make_room(b, 1); | 
					
						
							|  |  |  | 	b->buffer[b->len] = 0; | 
					
						
							|  |  |  | 	return b->buffer; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | void put_bytes(struct membuffer *b, const char *str, int len) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	make_room(b, len); | 
					
						
							| 
									
										
										
										
											2014-02-09 09:40:49 -08:00
										 |  |  | 	memcpy(b->buffer + b->len, str, len); | 
					
						
							|  |  |  | 	b->len += len; | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void put_string(struct membuffer *b, const char *str) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	put_bytes(b, str, strlen(str)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void put_vformat(struct membuffer *b, const char *fmt, va_list args) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-08 15:54:50 -08:00
										 |  |  | 	int room = 128; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (;;) { | 
					
						
							|  |  |  | 		int len; | 
					
						
							|  |  |  | 		va_list copy; | 
					
						
							|  |  |  | 		char *target; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		make_room(b, room); | 
					
						
							|  |  |  | 		room = b->alloc - b->len; | 
					
						
							|  |  |  | 		target = b->buffer + b->len; | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-08 15:54:50 -08:00
										 |  |  | 		va_copy(copy, args); | 
					
						
							|  |  |  | 		len = vsnprintf(target, room, fmt, copy); | 
					
						
							|  |  |  | 		va_end(copy); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (len < room) { | 
					
						
							|  |  |  | 			b->len += len; | 
					
						
							|  |  |  | 			return; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-06-02 20:10:54 +03:00
										 |  |  | 		room = len + 1; | 
					
						
							| 
									
										
										
										
											2014-03-08 15:54:50 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-01-24 12:47:26 +12:00
										 |  |  | /* Silly helper using membuffer */ | 
					
						
							|  |  |  | char *vformat_string(const char *fmt, va_list args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct membuffer mb = { 0 }; | 
					
						
							|  |  |  | 	put_vformat(&mb, fmt, args); | 
					
						
							|  |  |  | 	mb_cstring(&mb); | 
					
						
							|  |  |  | 	return detach_buffer(&mb); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | char *format_string(const char *fmt, ...) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	va_list args; | 
					
						
							|  |  |  | 	char *result; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	va_start(args, fmt); | 
					
						
							|  |  |  | 	result = vformat_string(fmt, args); | 
					
						
							|  |  |  | 	va_end(args); | 
					
						
							|  |  |  | 	return result; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | void put_format(struct membuffer *b, const char *fmt, ...) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	va_list args; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	va_start(args, fmt); | 
					
						
							|  |  |  | 	put_vformat(b, fmt, args); | 
					
						
							|  |  |  | 	va_end(args); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void put_milli(struct membuffer *b, const char *pre, int value, const char *post) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	char buf[4]; | 
					
						
							|  |  |  | 	const char *sign = ""; | 
					
						
							|  |  |  | 	unsigned v; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	v = value; | 
					
						
							|  |  |  | 	if (value < 0) { | 
					
						
							|  |  |  | 		sign = "-"; | 
					
						
							|  |  |  | 		v = -value; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for (i = 2; i >= 0; i--) { | 
					
						
							|  |  |  | 		buf[i] = (v % 10) + '0'; | 
					
						
							|  |  |  | 		v /= 10; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	buf[3] = 0; | 
					
						
							|  |  |  | 	if (buf[2] == '0') { | 
					
						
							|  |  |  | 		buf[2] = 0; | 
					
						
							|  |  |  | 		if (buf[1] == '0') | 
					
						
							|  |  |  | 			buf[1] = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	put_format(b, "%s%s%u.%s%s", pre, sign, v, buf, post); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | void put_temperature(struct membuffer *b, temperature_t temp, const char *pre, const char *post) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | 	if (temp.mkelvin) | 
					
						
							|  |  |  | 		put_milli(b, pre, temp.mkelvin - ZERO_C_IN_MKELVIN, post); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | void put_depth(struct membuffer *b, depth_t depth, const char *pre, const char *post) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | 	if (depth.mm) | 
					
						
							|  |  |  | 		put_milli(b, pre, depth.mm, post); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | void put_duration(struct membuffer *b, duration_t duration, const char *pre, const char *post) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | 	if (duration.seconds) | 
					
						
							|  |  |  | 		put_format(b, "%s%u:%02u%s", pre, FRACTION(duration.seconds, 60), post); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | void put_pressure(struct membuffer *b, pressure_t pressure, const char *pre, const char *post) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | 	if (pressure.mbar) | 
					
						
							|  |  |  | 		put_milli(b, pre, pressure.mbar, post); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | void put_salinity(struct membuffer *b, int salinity, const char *pre, const char *post) | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2014-03-07 20:33:22 -08:00
										 |  |  | 	if (salinity) | 
					
						
							|  |  |  | 		put_format(b, "%s%d%s", pre, salinity / 10, post); | 
					
						
							| 
									
										
										
										
											2014-01-16 09:03:11 +07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2014-04-05 13:01:34 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | void put_degrees(struct membuffer *b, degrees_t value, const char *pre, const char *post) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int udeg = value.udeg; | 
					
						
							|  |  |  | 	const char *sign = ""; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (udeg < 0) { | 
					
						
							|  |  |  | 		udeg = -udeg; | 
					
						
							|  |  |  | 		sign = "-"; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-06-02 20:10:54 +03:00
										 |  |  | 	put_format(b, "%s%s%u.%06u%s", pre, sign, FRACTION(udeg, 1000000), post); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void put_quoted(struct membuffer *b, const char *text, int is_attribute, int is_html) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const char *p = text; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-10-08 19:17:39 +03:00
										 |  |  | 	for (;text;) { | 
					
						
							| 
									
										
										
										
											2014-06-02 20:10:54 +03:00
										 |  |  | 		const char *escape; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		switch (*p++) { | 
					
						
							|  |  |  | 		default: | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		case 0: | 
					
						
							|  |  |  | 			escape = NULL; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case 1 ... 8: | 
					
						
							|  |  |  | 		case 11: | 
					
						
							|  |  |  | 		case 12: | 
					
						
							|  |  |  | 		case 14 ... 31: | 
					
						
							|  |  |  | 			escape = "?"; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '<': | 
					
						
							|  |  |  | 			escape = "<"; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '>': | 
					
						
							|  |  |  | 			escape = ">"; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '&': | 
					
						
							|  |  |  | 			escape = "&"; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '\'': | 
					
						
							|  |  |  | 			if (!is_attribute) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			escape = "'"; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '\"': | 
					
						
							|  |  |  | 			if (!is_attribute) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			escape = """; | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		case '\n': | 
					
						
							|  |  |  | 			if (!is_html) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				escape = "<br>"; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		put_bytes(b, text, (p - text - 1)); | 
					
						
							|  |  |  | 		if (!escape) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		put_string(b, escape); | 
					
						
							|  |  |  | 		text = p; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-04-05 13:01:34 -07:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2015-02-14 17:48:19 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | char *add_to_string_va(const char *old, const char *fmt, va_list args) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *res; | 
					
						
							|  |  |  | 	struct membuffer o = { 0 }, n = { 0 }; | 
					
						
							|  |  |  | 	put_vformat(&n, fmt, args); | 
					
						
							|  |  |  | 	put_format(&o, "%s\n%s", old ?: "", mb_cstring(&n)); | 
					
						
							|  |  |  | 	res = strdup(mb_cstring(&o)); | 
					
						
							|  |  |  | 	free_buffer(&o); | 
					
						
							|  |  |  | 	free_buffer(&n); | 
					
						
							|  |  |  | 	free((void *)old); | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* this is a convenience function that cleverly adds text to a string, using our membuffer
 | 
					
						
							|  |  |  |  * infrastructure. | 
					
						
							|  |  |  |  * WARNING - this will free(old), the intended pattern is | 
					
						
							|  |  |  |  * string = add_to_string(string, fmt, ...) | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | char *add_to_string(const char *old, const char *fmt, ...) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	char *res; | 
					
						
							|  |  |  | 	va_list args; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	va_start(args, fmt); | 
					
						
							|  |  |  | 	res = add_to_string_va(old, fmt, args); | 
					
						
							|  |  |  | 	va_end(args); | 
					
						
							|  |  |  | 	return res; | 
					
						
							|  |  |  | } |