mirror of
				https://github.com/subsurface/subsurface.git
				synced 2025-02-19 22:16:15 +00:00 
			
		
		
		
	Exporting to divelogs.de triggered this bug when divesite name is empty. Fixes #656 Signed-off-by: Miika Turkia <miika.turkia@gmail.com>
		
			
				
	
	
		
			291 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			291 lines
		
	
	
	
		
			5.5 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| // SPDX-License-Identifier: GPL-2.0
 | |
| #ifdef __clang__
 | |
| // Clang has a bug on zero-initialization of C structs.
 | |
| #pragma clang diagnostic ignored "-Wmissing-field-initializers"
 | |
| #endif
 | |
| 
 | |
| #include <stdarg.h>
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <stdio.h>
 | |
| 
 | |
| #include "dive.h"
 | |
| #include "membuffer.h"
 | |
| 
 | |
| char *detach_buffer(struct membuffer *b)
 | |
| {
 | |
| 	char *result = b->buffer;
 | |
| 	b->buffer = NULL;
 | |
| 	b->len = 0;
 | |
| 	b->alloc = 0;
 | |
| 	return result;
 | |
| }
 | |
| 
 | |
| void free_buffer(struct membuffer *b)
 | |
| {
 | |
| 	free(detach_buffer(b));
 | |
| }
 | |
| 
 | |
| void flush_buffer(struct membuffer *b, FILE *f)
 | |
| {
 | |
| 	if (b->len) {
 | |
| 		fwrite(b->buffer, 1, b->len, f);
 | |
| 		free_buffer(b);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| void strip_mb(struct membuffer *b)
 | |
| {
 | |
| 	while (b->len && isspace(b->buffer[b->len - 1]))
 | |
| 		b->len--;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * 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)
 | |
| {
 | |
| 	unsigned int needed = b->len + size;
 | |
| 	if (needed > b->alloc) {
 | |
| 		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;
 | |
| 		b->alloc = needed;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| const char *mb_cstring(struct membuffer *b)
 | |
| {
 | |
| 	make_room(b, 1);
 | |
| 	b->buffer[b->len] = 0;
 | |
| 	return b->buffer;
 | |
| }
 | |
| 
 | |
| void put_bytes(struct membuffer *b, const char *str, int len)
 | |
| {
 | |
| 	make_room(b, len);
 | |
| 	memcpy(b->buffer + b->len, str, len);
 | |
| 	b->len += len;
 | |
| }
 | |
| 
 | |
| 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)
 | |
| {
 | |
| 	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;
 | |
| 
 | |
| 		va_copy(copy, args);
 | |
| 		len = vsnprintf(target, room, fmt, copy);
 | |
| 		va_end(copy);
 | |
| 
 | |
| 		if (len < room) {
 | |
| 			b->len += len;
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		room = len + 1;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /* 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;
 | |
| }
 | |
| 
 | |
| 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);
 | |
| }
 | |
| 
 | |
| void put_temperature(struct membuffer *b, temperature_t temp, const char *pre, const char *post)
 | |
| {
 | |
| 	if (temp.mkelvin)
 | |
| 		put_milli(b, pre, temp.mkelvin - ZERO_C_IN_MKELVIN, post);
 | |
| }
 | |
| 
 | |
| void put_depth(struct membuffer *b, depth_t depth, const char *pre, const char *post)
 | |
| {
 | |
| 	if (depth.mm)
 | |
| 		put_milli(b, pre, depth.mm, post);
 | |
| }
 | |
| 
 | |
| void put_duration(struct membuffer *b, duration_t duration, const char *pre, const char *post)
 | |
| {
 | |
| 	if (duration.seconds)
 | |
| 		put_format(b, "%s%u:%02u%s", pre, FRACTION(duration.seconds, 60), post);
 | |
| }
 | |
| 
 | |
| void put_pressure(struct membuffer *b, pressure_t pressure, const char *pre, const char *post)
 | |
| {
 | |
| 	if (pressure.mbar)
 | |
| 		put_milli(b, pre, pressure.mbar, post);
 | |
| }
 | |
| 
 | |
| void put_salinity(struct membuffer *b, int salinity, const char *pre, const char *post)
 | |
| {
 | |
| 	if (salinity)
 | |
| 		put_format(b, "%s%d%s", pre, salinity / 10, post);
 | |
| }
 | |
| 
 | |
| 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 = "-";
 | |
| 	}
 | |
| 	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;
 | |
| 
 | |
| 	for (;text;) {
 | |
| 		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;
 | |
| 	}
 | |
| }
 | |
| 
 | |
| 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;
 | |
| }
 |