aboutsummaryrefslogtreecommitdiffstats
path: root/core/tag.c
blob: 66dcfcef8329d3572fada87dfff0297b32c8a0b5 (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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
// SPDX-License-Identifier: GPL-2.0

#include "tag.h"
#include "structured_list.h"
#include "subsurface-string.h"
#include "membuffer.h"
#include "gettext.h"

#include <stdlib.h>

struct tag_entry *g_tag_list = NULL;

static const char *default_tags[] = {
	QT_TRANSLATE_NOOP("gettextFromC", "boat"), QT_TRANSLATE_NOOP("gettextFromC", "shore"), QT_TRANSLATE_NOOP("gettextFromC", "drift"),
	QT_TRANSLATE_NOOP("gettextFromC", "deep"), QT_TRANSLATE_NOOP("gettextFromC", "cavern"), QT_TRANSLATE_NOOP("gettextFromC", "ice"),
	QT_TRANSLATE_NOOP("gettextFromC", "wreck"), QT_TRANSLATE_NOOP("gettextFromC", "cave"), QT_TRANSLATE_NOOP("gettextFromC", "altitude"),
	QT_TRANSLATE_NOOP("gettextFromC", "pool"), QT_TRANSLATE_NOOP("gettextFromC", "lake"), QT_TRANSLATE_NOOP("gettextFromC", "river"),
	QT_TRANSLATE_NOOP("gettextFromC", "night"), QT_TRANSLATE_NOOP("gettextFromC", "fresh"), QT_TRANSLATE_NOOP("gettextFromC", "student"),
	QT_TRANSLATE_NOOP("gettextFromC", "instructor"), QT_TRANSLATE_NOOP("gettextFromC", "photo"), QT_TRANSLATE_NOOP("gettextFromC", "video"),
	QT_TRANSLATE_NOOP("gettextFromC", "deco")
};

/* copy an element in a list of tags */
static void copy_tl(struct tag_entry *st, struct tag_entry *dt)
{
	dt->tag = malloc(sizeof(struct divetag));
	dt->tag->name = copy_string(st->tag->name);
	dt->tag->source = copy_string(st->tag->source);
}

static bool tag_seen_before(struct tag_entry *start, struct tag_entry *before)
{
	while (start && start != before) {
		if (same_string(start->tag->name, before->tag->name))
			return true;
		start = start->next;
	}
	return false;
}

/* remove duplicates and empty nodes */
void taglist_cleanup(struct tag_entry **tag_list)
{
	struct tag_entry **tl = tag_list;
	while (*tl) {
		/* skip tags that are empty or that we have seen before */
		if (empty_string((*tl)->tag->name) || tag_seen_before(*tag_list, *tl)) {
			*tl = (*tl)->next;
			continue;
		}
		tl = &(*tl)->next;
	}
}

char *taglist_get_tagstring(struct tag_entry *tag_list)
{
	bool first_tag = true;
	struct membuffer b = { 0 };
	struct tag_entry *tmp = tag_list;
	while (tmp != NULL) {
		if (!empty_string(tmp->tag->name)) {
			if (first_tag) {
				put_format(&b, "%s", tmp->tag->name);
				first_tag = false;
			} else {
				put_format(&b, ", %s", tmp->tag->name);
			}
		}
		tmp = tmp->next;
	}
	/* Ensures we do return null terminated empty string for:
	 *  - empty tag list
	 *  - tag list with empty tag only
	 */
	return detach_cstring(&b);
}

static inline void taglist_free_divetag(struct divetag *tag)
{
	if (tag->name != NULL)
		free(tag->name);
	if (tag->source != NULL)
		free(tag->source);
	free(tag);
}

/* Add a tag to the tag_list, keep the list sorted */
static struct divetag *taglist_add_divetag(struct tag_entry **tag_list, struct divetag *tag)
{
	struct tag_entry *next, *entry;

	while ((next = *tag_list) != NULL) {
		int cmp = strcmp(next->tag->name, tag->name);

		/* Already have it? */
		if (!cmp)
			return next->tag;
		/* Is the entry larger? If so, insert here */
		if (cmp > 0)
			break;
		/* Continue traversing the list */
		tag_list = &next->next;
	}

	/* Insert in front of it */
	entry = malloc(sizeof(struct tag_entry));
	entry->next = next;
	entry->tag = tag;
	*tag_list = entry;
	return tag;
}

struct divetag *taglist_add_tag(struct tag_entry **tag_list, const char *tag)
{
	size_t i = 0;
	int is_default_tag = 0;
	struct divetag *ret_tag, *new_tag;
	const char *translation;
	new_tag = malloc(sizeof(struct divetag));

	for (i = 0; i < sizeof(default_tags) / sizeof(char *); i++) {
		if (strcmp(default_tags[i], tag) == 0) {
			is_default_tag = 1;
			break;
		}
	}
	/* Only translate default tags */
	if (is_default_tag) {
		translation = translate("gettextFromC", tag);
		new_tag->name = malloc(strlen(translation) + 1);
		memcpy(new_tag->name, translation, strlen(translation) + 1);
		new_tag->source = malloc(strlen(tag) + 1);
		memcpy(new_tag->source, tag, strlen(tag) + 1);
	} else {
		new_tag->source = NULL;
		new_tag->name = malloc(strlen(tag) + 1);
		memcpy(new_tag->name, tag, strlen(tag) + 1);
	}
	/* Try to insert new_tag into g_tag_list if we are not operating on it */
	if (tag_list != &g_tag_list) {
		ret_tag = taglist_add_divetag(&g_tag_list, new_tag);
		/* g_tag_list already contains new_tag, free the duplicate */
		if (ret_tag != new_tag)
			taglist_free_divetag(new_tag);
		ret_tag = taglist_add_divetag(tag_list, ret_tag);
	} else {
		ret_tag = taglist_add_divetag(tag_list, new_tag);
		if (ret_tag != new_tag)
			taglist_free_divetag(new_tag);
	}
	return ret_tag;
}

void taglist_free(struct tag_entry *entry)
{
	STRUCTURED_LIST_FREE(struct tag_entry, entry, free)
}

struct tag_entry *taglist_copy(struct tag_entry *s)
{
	struct tag_entry *res;
	STRUCTURED_LIST_COPY(struct tag_entry, s, res, copy_tl);
	return res;
}

/* Merge src1 and src2, write to *dst */
void taglist_merge(struct tag_entry **dst, struct tag_entry *src1, struct tag_entry *src2)
{
	struct tag_entry *entry;

	for (entry = src1; entry; entry = entry->next)
		taglist_add_divetag(dst, entry->tag);
	for (entry = src2; entry; entry = entry->next)
		taglist_add_divetag(dst, entry->tag);
}

void taglist_init_global()
{
	size_t i;

	for (i = 0; i < sizeof(default_tags) / sizeof(char *); i++)
		taglist_add_tag(&g_tag_list, default_tags[i]);
}

bool taglist_contains(struct tag_entry *tag_list, const char *tag)
{
	while (tag_list) {
		if (same_string(tag_list->tag->name, tag))
			return true;
		tag_list = tag_list->next;
	}
	return false;
}

struct tag_entry *taglist_added(struct tag_entry *original_list, struct tag_entry *new_list)
{
	struct tag_entry *added_list = NULL;
	while (new_list) {
		if (!taglist_contains(original_list, new_list->tag->name))
			taglist_add_tag(&added_list, new_list->tag->name);
		new_list = new_list->next;
	}
	return added_list;
}