]>
Commit | Line | Data |
---|---|---|
d973a13d MT |
1 | /*############################################################################# |
2 | # # | |
3 | # Pakfire - The IPFire package management system # | |
4 | # Copyright (C) 2022 Pakfire development team # | |
5 | # # | |
6 | # This program is free software: you can redistribute it and/or modify # | |
7 | # it under the terms of the GNU General Public License as published by # | |
8 | # the Free Software Foundation, either version 3 of the License, or # | |
9 | # (at your option) any later version. # | |
10 | # # | |
11 | # This program 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 # | |
14 | # GNU General Public License for more details. # | |
15 | # # | |
16 | # You should have received a copy of the GNU General Public License # | |
17 | # along with this program. If not, see <http://www.gnu.org/licenses/>. # | |
18 | # # | |
19 | #############################################################################*/ | |
20 | ||
21 | #include <errno.h> | |
73dab745 | 22 | #include <stdarg.h> |
d973a13d MT |
23 | #include <stdio.h> |
24 | #include <stdlib.h> | |
25 | #include <string.h> | |
26 | #include <time.h> | |
27 | ||
28 | #include <pakfire/string.h> | |
29 | ||
73dab745 MT |
30 | int __pakfire_string_format(char* s, const size_t length, const char* format, ...) { |
31 | va_list args; | |
cbedac26 | 32 | int r; |
73dab745 | 33 | |
cbedac26 | 34 | // Call __pakfire_string_vformat |
73dab745 | 35 | va_start(args, format); |
cbedac26 | 36 | r = __pakfire_string_vformat(s, length, format, args); |
73dab745 MT |
37 | va_end(args); |
38 | ||
cbedac26 MT |
39 | return r; |
40 | } | |
41 | ||
42 | int __pakfire_string_vformat(char* s, const size_t length, const char* format, va_list args) { | |
43 | // Write string to buffer | |
44 | const ssize_t required = vsnprintf(s, length, format, args); | |
45 | ||
73dab745 MT |
46 | // Catch any errors |
47 | if (required < 0) | |
48 | return 1; | |
49 | ||
50 | // Check if the entire string could be written | |
51 | if ((size_t)required >= length) { | |
52 | errno = ENOMEM; | |
53 | return 1; | |
54 | } | |
55 | ||
56 | // Success | |
57 | return 0; | |
58 | } | |
59 | ||
d973a13d MT |
60 | int pakfire_string_startswith(const char* s, const char* prefix) { |
61 | // Validate input | |
62 | if (!s || !prefix) { | |
63 | errno = EINVAL; | |
64 | return 1; | |
65 | } | |
66 | ||
67 | return !strncmp(s, prefix, strlen(prefix)); | |
68 | } | |
69 | ||
70 | int pakfire_string_endswith(const char* s, const char* suffix) { | |
71 | // Validate input | |
72 | if (!s || !suffix) { | |
73 | errno = EINVAL; | |
74 | return 1; | |
75 | } | |
76 | ||
77 | return !strcmp(s + strlen(s) - strlen(suffix), suffix); | |
78 | } | |
79 | ||
80 | int pakfire_string_matches(const char* s, const char* pattern) { | |
81 | // Validate input | |
82 | if (!s || !pattern) { | |
83 | errno = EINVAL; | |
84 | return 1; | |
85 | } | |
86 | ||
87 | return !!strstr(s, pattern); | |
88 | } | |
89 | ||
90 | int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) { | |
91 | char* p = strstr(s, delim); | |
92 | ||
93 | // Delim was not found | |
94 | if (!p) { | |
95 | *s1 = NULL; | |
96 | *s2 = NULL; | |
97 | return 1; | |
98 | } | |
99 | ||
100 | // Length of string before delim | |
101 | size_t l = p - s; | |
102 | ||
103 | char* buffer = malloc(l + 1); | |
104 | if (!buffer) | |
105 | return 1; | |
106 | ||
107 | // Copy first part | |
108 | *s1 = memcpy(buffer, s, l); | |
109 | buffer[l] = '\0'; | |
110 | ||
111 | // Copy second part | |
112 | *s2 = strdup(p + strlen(delim)); | |
113 | ||
114 | return 0; | |
115 | } | |
116 | ||
117 | char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) { | |
118 | // Return NULL on no input or no pattern | |
119 | if (!s || !pattern) { | |
120 | errno = EINVAL; | |
121 | return NULL; | |
122 | } | |
123 | ||
124 | // Replace with nothing when repl is NULL | |
125 | if (!repl) | |
126 | repl = ""; | |
127 | ||
128 | char* result = NULL; | |
129 | const char** cache = NULL; | |
130 | unsigned int count = 0; | |
131 | ||
132 | const size_t pattern_length = strlen(pattern); | |
133 | ||
134 | // Working pointer | |
135 | const char* p = s; | |
136 | ||
137 | // Find all occurrences of pattern and store their location | |
138 | while (1) { | |
139 | const char* needle = strstr(p, pattern); | |
140 | if (!needle) | |
141 | break; | |
142 | ||
143 | // Make space in the cache | |
144 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
145 | cache[count++] = needle; | |
146 | ||
147 | // Move p forward | |
148 | p = needle + pattern_length; | |
149 | } | |
150 | ||
151 | // Copy the string if no occurence was found | |
152 | if (count == 0) { | |
153 | result = strdup(s); | |
154 | goto ERROR; | |
155 | } | |
156 | ||
157 | // Store the end pointer | |
158 | cache = reallocarray(cache, sizeof(*cache), count + 1); | |
159 | cache[count] = s + strlen(s); | |
160 | ||
161 | const size_t repl_length = strlen(repl); | |
162 | ||
163 | // Determine the length of the final string | |
164 | const size_t length = strlen(s) + ((repl_length - pattern_length) * count); | |
165 | ||
166 | // Allocate enough memory for the result | |
167 | result = malloc(length + 1); | |
168 | if (!result) | |
169 | goto ERROR; | |
170 | ||
171 | // Reset p | |
172 | p = s; | |
173 | ||
174 | // Working pointer for the result | |
175 | char* r = result; | |
176 | ||
177 | // Copy everything up to the first match | |
178 | ssize_t l = cache[0] - s; | |
179 | memcpy(r, p, l); | |
180 | r += l; | |
181 | p += l; | |
182 | ||
183 | for (unsigned int i = 0; i < count; i++) { | |
184 | // Put replacement here | |
185 | memcpy(r, repl, repl_length); | |
186 | r += repl_length; | |
187 | p += pattern_length; | |
188 | ||
189 | // Determine the length between two matches | |
190 | l = cache[i+1] - (cache[i] + pattern_length); | |
191 | ||
192 | memcpy(r, p, l); | |
193 | r += l; | |
194 | p += l; | |
195 | } | |
196 | ||
197 | // Terminate the string | |
198 | result[length] = '\0'; | |
199 | ||
200 | ERROR: | |
201 | if (cache) | |
202 | free(cache); | |
203 | ||
204 | return result; | |
205 | } | |
206 | ||
207 | static unsigned int pakfire_chrcnt(const char* s, char delim) { | |
208 | size_t length = strlen(s); | |
209 | ||
210 | unsigned int count = 0; | |
211 | ||
212 | for (unsigned int i = 0; i < length; i++) | |
213 | if (s[i] == delim) | |
214 | count++; | |
215 | ||
216 | return count; | |
217 | } | |
218 | ||
219 | char** pakfire_string_split(const char* s, char delim) { | |
220 | char** array = NULL; | |
221 | ||
222 | if (!s) { | |
223 | errno = EINVAL; | |
224 | return NULL; | |
225 | } | |
226 | ||
227 | // Count how often we need to split | |
228 | unsigned int count = pakfire_chrcnt(s, delim) + 1; | |
229 | ||
230 | // Allocate array | |
231 | array = calloc(count + 1, sizeof(*array)); | |
232 | if (!array) | |
233 | return NULL; | |
234 | ||
235 | // Copy string to stack | |
236 | char* p = strdupa(s); | |
237 | if (!p) | |
238 | return NULL; | |
239 | ||
240 | unsigned int i = 0; | |
241 | while (*p) { | |
242 | char* e = strchr(p, delim); | |
243 | ||
244 | // Terminate the string | |
245 | if (e) | |
246 | *e = '\0'; | |
247 | ||
248 | // Add string to the array | |
249 | array[i++] = strdup(p); | |
250 | ||
251 | // End loop when we reached the end | |
252 | if (!e) | |
253 | break; | |
254 | ||
255 | // Or continue at the next line | |
256 | p = e + 1; | |
257 | } | |
258 | ||
259 | return array; | |
260 | } | |
261 | ||
262 | char* pakfire_string_join(char** list, const char* delim) { | |
263 | // Validate input | |
264 | if (!list || !delim) { | |
265 | errno = EINVAL; | |
266 | return NULL; | |
267 | } | |
268 | ||
269 | size_t length = 0; | |
270 | unsigned int elements = 0; | |
271 | ||
272 | // Count the number of elements and the total length | |
273 | for (char** item = list; *item; item++) { | |
274 | length += strlen(*item); | |
275 | elements++; | |
276 | } | |
277 | ||
278 | // Empty list? | |
279 | if (!elements) | |
280 | return NULL; | |
281 | ||
282 | // Add the delimiters | |
283 | length += strlen(delim) * (elements - 1); | |
284 | ||
285 | // Allocate the result string | |
286 | char* string = malloc(length + 1); | |
287 | if (!string) | |
288 | return NULL; | |
289 | ||
290 | // Pointer to where we are writing | |
291 | char* p = string; | |
292 | ||
293 | size_t bytes_left = length + 1; | |
294 | size_t bytes_written; | |
295 | ||
296 | for (char** item = list; *item; item++) { | |
297 | bytes_written = snprintf(p, bytes_left, "%s", *item); | |
298 | ||
299 | bytes_left -= bytes_written; | |
300 | p += bytes_written; | |
301 | ||
302 | // Write the delimiter | |
303 | if (bytes_left) { | |
304 | bytes_written = snprintf(p, bytes_left, "%s", delim); | |
305 | ||
306 | bytes_left -= bytes_written; | |
307 | p += bytes_written; | |
308 | } | |
309 | } | |
310 | ||
311 | return string; | |
312 | } | |
313 | ||
314 | int __pakfire_format_size(char* dst, size_t length, double value) { | |
315 | const char* units[] = { | |
316 | "%.0f ", | |
317 | "%.0fk", | |
318 | "%.1fM", | |
319 | "%.1fG", | |
320 | "%.1fT", | |
321 | NULL | |
322 | }; | |
323 | const char** unit = units; | |
324 | ||
325 | while (*(unit + 1) && value >= 1024.0) { | |
326 | value /= 1024.0; | |
327 | unit++; | |
328 | } | |
329 | ||
1cd3e864 | 330 | return __pakfire_string_format(dst, length, *unit, value); |
d973a13d MT |
331 | } |
332 | ||
333 | int pakfire_format_speed(char* dst, size_t length, double value) { | |
334 | const char* units[] = { | |
335 | "%4.0fB/s", | |
336 | "%4.0fkB/s", | |
337 | "%4.1fMB/s", | |
338 | "%4.1fGB/s", | |
339 | "%4.1fTB/s", | |
340 | NULL | |
341 | }; | |
342 | const char** unit = units; | |
343 | ||
344 | while (*(unit + 1) && value >= 1024.0) { | |
345 | value /= 1024.0; | |
346 | unit++; | |
347 | } | |
348 | ||
1cd3e864 | 349 | return __pakfire_string_format(dst, length, *unit, value); |
d973a13d MT |
350 | } |
351 | ||
5cf4a7a1 MT |
352 | int __pakfire_strftime(char* buffer, const size_t length, |
353 | const char* format, const time_t t) { | |
d973a13d | 354 | struct tm* tm = gmtime(&t); |
5cf4a7a1 | 355 | int r; |
d973a13d | 356 | |
5cf4a7a1 MT |
357 | // Format time |
358 | r = strftime(buffer, length, format, tm); | |
359 | if (r == 0) | |
360 | return 1; | |
d973a13d | 361 | |
5cf4a7a1 | 362 | return 0; |
d973a13d | 363 | } |
d973a13d | 364 | |
5cf4a7a1 | 365 | int __pakfire_strftime_now(char* buffer, size_t length, const char* format) { |
d973a13d MT |
366 | // Fetch the current time |
367 | const time_t t = time(NULL); | |
368 | if (t < 0) | |
369 | return 1; | |
370 | ||
5cf4a7a1 | 371 | return __pakfire_strftime(buffer, length, format, t); |
d973a13d | 372 | } |
528b1f1c MT |
373 | |
374 | size_t pakfire_string_parse_bytes(const char* s) { | |
375 | const char* units = "kMGT"; | |
376 | char* unit = NULL; | |
377 | ||
378 | // Read the numeric value | |
379 | size_t bytes = strtoul(s, &unit, 10); | |
380 | ||
381 | // Return bytes if there is no unit | |
382 | if (!unit || !*unit) | |
383 | return bytes; | |
384 | ||
385 | // Is the unit of the correct length? | |
386 | const size_t length = strlen(unit); | |
387 | if (length > 1) | |
388 | goto ERROR; | |
389 | ||
390 | // Walk through all units and find a match | |
391 | for (const char* u = units; *u; u++) { | |
392 | bytes *= 1024; | |
393 | ||
394 | // End if the unit matches | |
395 | if (*unit == *u) | |
396 | return bytes; | |
397 | } | |
398 | ||
399 | ERROR: | |
400 | errno = EINVAL; | |
401 | return 0; | |
402 | } |