summaryrefslogtreecommitdiffstats
path: root/file.c
blob: 2e46f94fd1ac2009a707507e8abdbee98b2bb7ad (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>

#include "dive.h"

struct memblock {
	void *buffer;
	size_t size;
};

static int readfile(const char *filename, struct memblock *mem)
{
	int ret, fd = open(filename, O_RDONLY);
	struct stat st;
	char *buf;

	mem->buffer = NULL;
	mem->size = 0;

	fd = open(filename, O_RDONLY);
	if (fd < 0)
		return fd;
	ret = fstat(fd, &st);
	if (ret < 0)
		goto out;
	ret = -EINVAL;
	if (!S_ISREG(st.st_mode))
		goto out;
	ret = 0;
	if (!st.st_size)
		goto out;
	buf = malloc(st.st_size+1);
	ret = -1;
	errno = ENOMEM;
	if (!buf)
		goto out;
	mem->buffer = buf;
	mem->size = st.st_size;
	ret = read(fd, buf, mem->size);
	if (ret < 0)
		goto free;
	buf[ret] = 0;
	if (ret == mem->size)
		goto out;
	errno = EIO;
	ret = -1;
free:
	free(mem->buffer);
	mem->buffer = NULL;
	mem->size = 0;
out:
	close(fd);
	return ret;
}

#ifdef LIBZIP
#include <zip.h>

static void suunto_read(struct zip_file *file, GError **error)
{
	int size = 1024, n, read = 0;
	char *mem = malloc(size);

	while ((n = zip_fread(file, mem+read, size-read)) > 0) {
		read += n;
		size = read * 3 / 2;
		mem = realloc(mem, size);
	}
	parse_xml_buffer("SDE file", mem, read, error);
	free(mem);
}
#endif

static int try_to_open_suunto(const char *filename, struct memblock *mem, GError **error)
{
	int success = 0;
#ifdef LIBZIP
	/* Grr. libzip needs to re-open the file, it can't take a buffer */
	struct zip *zip = zip_open(filename, ZIP_CHECKCONS, NULL);

	if (zip) {
		int index;
		for (index = 0; ;index++) {
			struct zip_file *file = zip_fopen_index(zip, index, 0);
			if (!file)
				break;
			suunto_read(file, error);
			zip_fclose(file);
			success++;
		}
		zip_close(zip);
	}
#endif
	return success;
}

static int open_by_filename(const char *filename, const char *fmt, struct memblock *mem, GError **error)
{
	/* Suunto Dive Manager files: SDE */
	if (!strcasecmp(fmt, "SDE"))
		return try_to_open_suunto(filename, mem, error);

	return 0;
}

static void parse_file_buffer(const char *filename, struct memblock *mem, GError **error)
{
	char *fmt = strrchr(filename, '.');
	if (fmt && open_by_filename(filename, fmt+1, mem, error))
		return;

	parse_xml_buffer(filename, mem->buffer, mem->size, error);
}

void parse_file(const char *filename, GError **error)
{
	struct memblock mem;

	if (readfile(filename, &mem) < 0) {
		fprintf(stderr, "Failed to read '%s'.\n", filename);
		if (error) {
			*error = g_error_new(g_quark_from_string("subsurface"),
					     DIVE_ERROR_PARSE,
					     "Failed to read '%s'",
					     filename);
		}
		return;
	}

	parse_file_buffer(filename, &mem, error);
	free(mem.buffer);
}