/src/libxmlb/src/xb-string.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2018 Richard Hughes <richard@hughsie.com> |
3 | | * |
4 | | * SPDX-License-Identifier: LGPL-2.1-or-later |
5 | | */ |
6 | | |
7 | 0 | #define G_LOG_DOMAIN "XbSilo" |
8 | | |
9 | | #include "config.h" |
10 | | |
11 | | #include <gio/gio.h> |
12 | | #include <string.h> |
13 | | |
14 | | #include "xb-string-private.h" |
15 | | |
16 | | /** |
17 | | * xb_string_replace: (skip) |
18 | | * @str: The #GString to operate on |
19 | | * @search: The text to search for |
20 | | * @replace: The text to use for substitutions |
21 | | * |
22 | | * Performs multiple search and replace operations on the given string. |
23 | | * |
24 | | * Returns: the number of replacements done, or 0 if @search is not found. |
25 | | **/ |
26 | | guint |
27 | | xb_string_replace(GString *str, const gchar *search, const gchar *replace) |
28 | 6.55M | { |
29 | 6.55M | gchar *tmp; |
30 | 6.55M | guint count = 0; |
31 | 6.55M | gsize search_idx = 0; |
32 | 6.55M | gsize replace_len; |
33 | 6.55M | gsize search_len; |
34 | | |
35 | 6.55M | g_return_val_if_fail(str != NULL, 0); |
36 | 6.55M | g_return_val_if_fail(search != NULL, 0); |
37 | 6.55M | g_return_val_if_fail(replace != NULL, 0); |
38 | | |
39 | | /* nothing to do */ |
40 | 6.55M | if (str->len == 0) |
41 | 0 | return 0; |
42 | | |
43 | 6.55M | search_len = strlen(search); |
44 | 6.55M | replace_len = strlen(replace); |
45 | | |
46 | 6.55M | do { |
47 | 6.55M | tmp = g_strstr_len(str->str + search_idx, -1, search); |
48 | 6.55M | if (tmp == NULL) |
49 | 6.55M | break; |
50 | | |
51 | | /* advance the counter in case @replace contains @search */ |
52 | 1.27k | search_idx = (gsize)(tmp - str->str); |
53 | | |
54 | | /* reallocate the string if required */ |
55 | 1.27k | if (search_len > replace_len) { |
56 | 0 | g_string_erase(str, (gssize)search_idx, (gssize)(search_len - replace_len)); |
57 | 0 | memcpy(tmp, replace, replace_len); |
58 | 1.27k | } else if (search_len < replace_len) { |
59 | 1.27k | g_string_insert_len(str, |
60 | 1.27k | (gssize)search_idx, |
61 | 1.27k | replace, |
62 | 1.27k | (gssize)(replace_len - search_len)); |
63 | | /* we have to treat this specially as it could have |
64 | | * been reallocated when the insertion happened */ |
65 | 1.27k | memcpy(str->str + search_idx, replace, replace_len); |
66 | 1.27k | } else { |
67 | | /* just memcmp in the new string */ |
68 | 0 | memcpy(tmp, replace, replace_len); |
69 | 0 | } |
70 | 1.27k | search_idx += replace_len; |
71 | 1.27k | count++; |
72 | 1.27k | } while (TRUE); |
73 | | |
74 | 0 | return count; |
75 | 6.55M | } |
76 | | |
77 | | /** |
78 | | * xb_string_append_union: |
79 | | * @xpath: The #GString to operate on |
80 | | * @fmt: The format string |
81 | | * @...: varargs for @fmt |
82 | | * |
83 | | * Appends an XPath query into the string, automatically adding the union |
84 | | * operator (`|`) if required. |
85 | | * |
86 | | * Since: 0.1.2 |
87 | | **/ |
88 | | void |
89 | | xb_string_append_union(GString *xpath, const gchar *fmt, ...) |
90 | 0 | { |
91 | 0 | va_list args; |
92 | |
|
93 | 0 | g_return_if_fail(xpath != NULL); |
94 | 0 | g_return_if_fail(fmt != NULL); |
95 | | |
96 | 0 | if (xpath->len > 0) |
97 | 0 | g_string_append(xpath, "|"); |
98 | 0 | va_start(args, fmt); |
99 | 0 | #pragma clang diagnostic ignored "-Wformat-nonliteral" |
100 | 0 | g_string_append_vprintf(xpath, fmt, args); |
101 | 0 | #pragma clang diagnostic pop |
102 | 0 | va_end(args); |
103 | 0 | } |
104 | | |
105 | | /** |
106 | | * xb_string_contains: (skip) |
107 | | * @text: The source string |
108 | | * @search: The text to search for |
109 | | * |
110 | | * Searches for a substring match. |
111 | | * |
112 | | * Returns: %TRUE if the string @search is contained in @text. |
113 | | **/ |
114 | | gboolean |
115 | | xb_string_contains(const gchar *text, const gchar *search) |
116 | 0 | { |
117 | 0 | guint search_sz; |
118 | 0 | guint text_sz; |
119 | | |
120 | | /* can't possibly match */ |
121 | 0 | if (text == NULL || search == NULL) |
122 | 0 | return FALSE; |
123 | | |
124 | | /* sanity check */ |
125 | 0 | text_sz = strlen(text); |
126 | 0 | search_sz = strlen(search); |
127 | 0 | if (search_sz > text_sz) |
128 | 0 | return FALSE; |
129 | 0 | for (guint i = 0; i < text_sz - search_sz + 1; i++) { |
130 | 0 | if (strncmp(text + i, search, search_sz) == 0) |
131 | 0 | return TRUE; |
132 | 0 | } |
133 | 0 | return FALSE; |
134 | 0 | } |
135 | | |
136 | | /** |
137 | | * xb_string_search: (skip) |
138 | | * @text: The source string |
139 | | * @search: The text to search for |
140 | | * |
141 | | * Searches for a fuzzy search match, ignoring search matches that are not at |
142 | | * the start of the token. |
143 | | * |
144 | | * For example, `foo` and `baz` would match `foobar baz` but `bar` would not |
145 | | * match the same string. |
146 | | * |
147 | | * Returns: %TRUE if the string @search is contained in @text. |
148 | | * |
149 | | * Since: 0.3.0 |
150 | | **/ |
151 | | gboolean |
152 | | xb_string_search(const gchar *text, const gchar *search) |
153 | 0 | { |
154 | 0 | guint search_sz; |
155 | 0 | guint text_sz; |
156 | 0 | gboolean is_sow = TRUE; |
157 | | |
158 | | /* can't possibly match */ |
159 | 0 | if (text == NULL || text[0] == '\0') |
160 | 0 | return FALSE; |
161 | 0 | if (search == NULL || search[0] == '\0') |
162 | 0 | return FALSE; |
163 | | |
164 | | /* sanity check */ |
165 | 0 | text_sz = strlen(text); |
166 | 0 | search_sz = strlen(search); |
167 | 0 | if (search_sz > text_sz) |
168 | 0 | return FALSE; |
169 | 0 | for (guint i = 0; i < text_sz - search_sz + 1; i++) { |
170 | 0 | if (!g_ascii_isalnum(text[i])) { |
171 | 0 | is_sow = TRUE; |
172 | 0 | continue; |
173 | 0 | } |
174 | 0 | if (!is_sow) |
175 | 0 | continue; |
176 | 0 | if (g_ascii_strncasecmp(text + i, search, search_sz) == 0) |
177 | 0 | return TRUE; |
178 | | /* no longer the start of the word */ |
179 | 0 | is_sow = FALSE; |
180 | 0 | } |
181 | 0 | return FALSE; |
182 | 0 | } |
183 | | |
184 | | /** |
185 | | * xb_string_searchv: (skip) |
186 | | * @text: NULL-terminated source strings |
187 | | * @search: NULL-terminated text tokens to search for |
188 | | * |
189 | | * Searches for a fuzzy search match, ignoring search matches that are not at |
190 | | * the start of the token. |
191 | | * |
192 | | * For example `{"foo", "baz"}` would match with `{"wizz", "foobar"}` but |
193 | | * `{"baz"}` would not match the same set of strings. |
194 | | * |
195 | | * Returns: %TRUE if the string @search is contained in @text. |
196 | | * |
197 | | * Since: 0.3.0 |
198 | | **/ |
199 | | gboolean |
200 | | xb_string_searchv(const gchar **text, const gchar **search) |
201 | 0 | { |
202 | 0 | if (text == NULL || text[0] == NULL || text[0][0] == '\0') |
203 | 0 | return FALSE; |
204 | 0 | if (search == NULL || search[0] == NULL || search[0][0] == '\0') |
205 | 0 | return FALSE; |
206 | 0 | for (guint j = 0; text[j] != NULL; j++) { |
207 | 0 | for (guint i = 0; search[i] != NULL; i++) { |
208 | 0 | if (g_str_has_prefix(text[j], search[i])) |
209 | 0 | return TRUE; |
210 | 0 | } |
211 | 0 | } |
212 | 0 | return FALSE; |
213 | 0 | } |
214 | | |
215 | | /** |
216 | | * xb_string_token_valid: (skip) |
217 | | * @text: The potential token |
218 | | * @see_also: xb_builder_node_tokenize_text(), xb_builder_node_get_tokens() |
219 | | * |
220 | | * This is typically used to eliminate tokens which are not useful for search |
221 | | * matching. |
222 | | * |
223 | | * For instance, tokens like `in` and `at` are less than three characters in |
224 | | * size and should be rejected. |
225 | | * |
226 | | * Returns: %TRUE if the token should be used for searching. |
227 | | **/ |
228 | | gboolean |
229 | | xb_string_token_valid(const gchar *text) |
230 | 0 | { |
231 | 0 | if (text == NULL) |
232 | 0 | return FALSE; |
233 | 0 | if (text[0] == '\0' || text[1] == '\0' || text[2] == '\0') |
234 | 0 | return FALSE; |
235 | 0 | return TRUE; |
236 | 0 | } |
237 | | |
238 | | /** |
239 | | * xb_string_escape: |
240 | | * @str: string, e.g. `app/org.gnome.ghex/x86_64/stable` |
241 | | * |
242 | | * Escapes XPath control sequences such as newlines, tabs, and forward slashes. |
243 | | * |
244 | | * Returns: (transfer full): new string that is safe to use for queries |
245 | | * |
246 | | * Since: 0.1.2 |
247 | | **/ |
248 | | gchar * |
249 | | xb_string_escape(const gchar *str) |
250 | 0 | { |
251 | 0 | GString *tmp = g_string_new(str); |
252 | 0 | xb_string_replace(tmp, "/", "\\/"); |
253 | 0 | xb_string_replace(tmp, "\t", "\\t"); |
254 | 0 | xb_string_replace(tmp, "\n", "\\n"); |
255 | 0 | return g_string_free(tmp, FALSE); |
256 | 0 | } |
257 | | |
258 | | gchar * |
259 | | xb_string_xml_escape(const gchar *str) |
260 | 1.63M | { |
261 | 1.63M | GString *tmp = g_string_new(str); |
262 | 1.63M | xb_string_replace(tmp, "&", "&"); |
263 | 1.63M | xb_string_replace(tmp, "<", "<"); |
264 | 1.63M | xb_string_replace(tmp, ">", ">"); |
265 | 1.63M | xb_string_replace(tmp, "\"", """); |
266 | 1.63M | return g_string_free(tmp, FALSE); |
267 | 1.63M | } |
268 | | |
269 | | /* private */ |
270 | | gboolean |
271 | | xb_string_isspace(const gchar *str, gssize strsz) |
272 | 667k | { |
273 | 667k | gsize strsz_safe; |
274 | 667k | if (str == NULL) |
275 | 0 | return TRUE; |
276 | 667k | strsz_safe = strsz >= 0 ? (gsize)strsz : strlen(str); |
277 | 669k | for (gsize i = 0; i < strsz_safe; i++) { |
278 | 669k | if (!g_ascii_isspace(str[i])) |
279 | 667k | return FALSE; |
280 | 669k | } |
281 | 0 | return TRUE; |
282 | 667k | } |
283 | | |
284 | | void |
285 | | xb_guid_compute_for_data(XbGuid *out, const guint8 *buf, gsize bufsz) |
286 | 0 | { |
287 | 0 | guint8 buf_tmp[20] = {0x0}; |
288 | 0 | gsize buf_tmpsz = sizeof(buf_tmp); |
289 | 0 | g_autoptr(GChecksum) checksum = g_checksum_new(G_CHECKSUM_SHA1); |
290 | 0 | if (buf != NULL && bufsz != 0) |
291 | 0 | g_checksum_update(checksum, (const guchar *)buf, bufsz); |
292 | 0 | g_checksum_get_digest(checksum, buf_tmp, &buf_tmpsz); |
293 | 0 | memcpy(out, buf_tmp, sizeof(XbGuid)); |
294 | 0 | } |
295 | | |
296 | | gchar * |
297 | | xb_guid_to_string(XbGuid *guid) |
298 | 0 | { |
299 | 0 | return g_strdup_printf("%08x-%04x-%04x-%04x-%02x%02x%02x%02x%02x%02x", |
300 | 0 | (guint)GUINT32_TO_BE(guid->tlo), |
301 | 0 | (guint)GUINT16_TO_BE(guid->tmi), |
302 | 0 | (guint)GUINT16_TO_BE(guid->thi), |
303 | 0 | (guint)GUINT16_TO_BE(guid->clo), |
304 | 0 | guid->nde[0], |
305 | 0 | guid->nde[1], |
306 | 0 | guid->nde[2], |
307 | 0 | guid->nde[3], |
308 | 0 | guid->nde[4], |
309 | 0 | guid->nde[5]); |
310 | 0 | } |