]> git.ipfire.org Git - people/ms/libloc.git/blob - src/country-list.c
Don't abuse errno as return code
[people/ms/libloc.git] / src / country-list.c
1 /*
2 libloc - A library to determine the location of someone on the Internet
3
4 Copyright (C) 2020 IPFire Development Team <info@ipfire.org>
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15 */
16
17 #include <errno.h>
18 #include <stdlib.h>
19
20 #include <libloc/country.h>
21 #include <libloc/country-list.h>
22 #include <libloc/private.h>
23
24 struct loc_country_list {
25 struct loc_ctx* ctx;
26 int refcount;
27
28 struct loc_country** elements;
29 size_t elements_size;
30
31 size_t size;
32 };
33
34 static int loc_country_list_grow(struct loc_country_list* list) {
35 size_t size = list->elements_size * 2;
36 if (size < 1024)
37 size = 1024;
38
39 DEBUG(list->ctx, "Growing country list %p by %zu to %zu\n",
40 list, size, list->elements_size + size);
41
42 struct loc_country** elements = reallocarray(list->elements,
43 list->elements_size + size, sizeof(*list->elements));
44 if (!elements)
45 return 1;
46
47 list->elements = elements;
48 list->elements_size += size;
49
50 return 0;
51 }
52
53 LOC_EXPORT int loc_country_list_new(struct loc_ctx* ctx,
54 struct loc_country_list** list) {
55 struct loc_country_list* l = calloc(1, sizeof(*l));
56 if (!l)
57 return -ENOMEM;
58
59 l->ctx = loc_ref(ctx);
60 l->refcount = 1;
61
62 DEBUG(l->ctx, "Country list allocated at %p\n", l);
63 *list = l;
64
65 return 0;
66 }
67
68 LOC_EXPORT struct loc_country_list* loc_country_list_ref(struct loc_country_list* list) {
69 list->refcount++;
70
71 return list;
72 }
73
74 static void loc_country_list_free(struct loc_country_list* list) {
75 DEBUG(list->ctx, "Releasing country list at %p\n", list);
76
77 loc_country_list_clear(list);
78
79 loc_unref(list->ctx);
80 free(list);
81 }
82
83 LOC_EXPORT struct loc_country_list* loc_country_list_unref(struct loc_country_list* list) {
84 if (!list)
85 return NULL;
86
87 if (--list->refcount > 0)
88 return list;
89
90 loc_country_list_free(list);
91 return NULL;
92 }
93
94 LOC_EXPORT size_t loc_country_list_size(struct loc_country_list* list) {
95 return list->size;
96 }
97
98 LOC_EXPORT int loc_country_list_empty(struct loc_country_list* list) {
99 return list->size == 0;
100 }
101
102 LOC_EXPORT void loc_country_list_clear(struct loc_country_list* list) {
103 if (!list->elements)
104 return;
105
106 for (unsigned int i = 0; i < list->size; i++)
107 loc_country_unref(list->elements[i]);
108
109 free(list->elements);
110 list->elements = NULL;
111 list->elements_size = 0;
112
113 list->size = 0;
114 }
115
116 LOC_EXPORT struct loc_country* loc_country_list_get(struct loc_country_list* list, size_t index) {
117 // Check index
118 if (index >= list->size)
119 return NULL;
120
121 return loc_country_ref(list->elements[index]);
122 }
123
124 LOC_EXPORT int loc_country_list_append(
125 struct loc_country_list* list, struct loc_country* country) {
126 if (loc_country_list_contains(list, country))
127 return 0;
128
129 // Check if we have space left
130 if (list->size >= list->elements_size) {
131 int r = loc_country_list_grow(list);
132 if (r)
133 return r;
134 }
135
136 DEBUG(list->ctx, "%p: Appending country %p to list\n", list, country);
137
138 list->elements[list->size++] = loc_country_ref(country);
139
140 return 0;
141 }
142
143 LOC_EXPORT int loc_country_list_contains(
144 struct loc_country_list* list, struct loc_country* country) {
145 for (unsigned int i = 0; i < list->size; i++) {
146 if (loc_country_cmp(country, list->elements[i]) == 0)
147 return 1;
148 }
149
150 return 0;
151 }
152
153 LOC_EXPORT int loc_country_list_contains_code(
154 struct loc_country_list* list, const char* code) {
155 struct loc_country* country;
156
157 int r = loc_country_new(list->ctx, &country, code);
158 if (r) {
159 // Ignore invalid country codes which would never match
160 if (errno == EINVAL)
161 return 0;
162
163 return r;
164 }
165
166 r = loc_country_list_contains(list, country);
167 loc_country_unref(country);
168
169 return r;
170 }
171
172 static int __loc_country_cmp(const void* country1, const void* country2) {
173 return loc_country_cmp(*(struct loc_country**)country1, *(struct loc_country**)country2);
174 }
175
176 LOC_EXPORT void loc_country_list_sort(struct loc_country_list* list) {
177 // Sort everything
178 qsort(list->elements, list->size, sizeof(*list->elements), __loc_country_cmp);
179 }