]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/string.c
pakfire_format_time(): Fix typo
[pakfire.git] / src / libpakfire / string.c
CommitLineData
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
30int __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
42int __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
37c9c092
MT
60int __pakfire_string_set(char* s, const size_t length, const char* value) {
61 // If value is NULL or an empty, we will overwrite the buffer with just zeros
62 if (!value || !*value) {
63 for (unsigned int i = 0; i < length; i++)
64 s[i] = '\0';
65
66 return 0;
67 }
68
69 // Otherwise just copy
70 return __pakfire_string_format(s, length, "%s", value);
71}
72
d973a13d
MT
73int pakfire_string_startswith(const char* s, const char* prefix) {
74 // Validate input
75 if (!s || !prefix) {
76 errno = EINVAL;
77 return 1;
78 }
79
80 return !strncmp(s, prefix, strlen(prefix));
81}
82
83int pakfire_string_endswith(const char* s, const char* suffix) {
84 // Validate input
85 if (!s || !suffix) {
86 errno = EINVAL;
87 return 1;
88 }
89
90 return !strcmp(s + strlen(s) - strlen(suffix), suffix);
91}
92
93int pakfire_string_matches(const char* s, const char* pattern) {
94 // Validate input
95 if (!s || !pattern) {
96 errno = EINVAL;
97 return 1;
98 }
99
100 return !!strstr(s, pattern);
101}
102
103int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) {
104 char* p = strstr(s, delim);
105
106 // Delim was not found
107 if (!p) {
108 *s1 = NULL;
109 *s2 = NULL;
110 return 1;
111 }
112
113 // Length of string before delim
114 size_t l = p - s;
115
116 char* buffer = malloc(l + 1);
117 if (!buffer)
118 return 1;
119
120 // Copy first part
121 *s1 = memcpy(buffer, s, l);
122 buffer[l] = '\0';
123
124 // Copy second part
125 *s2 = strdup(p + strlen(delim));
126
127 return 0;
128}
129
130char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) {
131 // Return NULL on no input or no pattern
132 if (!s || !pattern) {
133 errno = EINVAL;
134 return NULL;
135 }
136
137 // Replace with nothing when repl is NULL
138 if (!repl)
139 repl = "";
140
141 char* result = NULL;
142 const char** cache = NULL;
143 unsigned int count = 0;
144
145 const size_t pattern_length = strlen(pattern);
146
147 // Working pointer
148 const char* p = s;
149
150 // Find all occurrences of pattern and store their location
151 while (1) {
152 const char* needle = strstr(p, pattern);
153 if (!needle)
154 break;
155
156 // Make space in the cache
157 cache = reallocarray(cache, sizeof(*cache), count + 1);
158 cache[count++] = needle;
159
160 // Move p forward
161 p = needle + pattern_length;
162 }
163
164 // Copy the string if no occurence was found
165 if (count == 0) {
166 result = strdup(s);
167 goto ERROR;
168 }
169
170 // Store the end pointer
171 cache = reallocarray(cache, sizeof(*cache), count + 1);
172 cache[count] = s + strlen(s);
173
174 const size_t repl_length = strlen(repl);
175
176 // Determine the length of the final string
177 const size_t length = strlen(s) + ((repl_length - pattern_length) * count);
178
179 // Allocate enough memory for the result
180 result = malloc(length + 1);
181 if (!result)
182 goto ERROR;
183
184 // Reset p
185 p = s;
186
187 // Working pointer for the result
188 char* r = result;
189
190 // Copy everything up to the first match
191 ssize_t l = cache[0] - s;
192 memcpy(r, p, l);
193 r += l;
194 p += l;
195
196 for (unsigned int i = 0; i < count; i++) {
197 // Put replacement here
198 memcpy(r, repl, repl_length);
199 r += repl_length;
200 p += pattern_length;
201
202 // Determine the length between two matches
203 l = cache[i+1] - (cache[i] + pattern_length);
204
205 memcpy(r, p, l);
206 r += l;
207 p += l;
208 }
209
210 // Terminate the string
211 result[length] = '\0';
212
213ERROR:
214 if (cache)
215 free(cache);
216
217 return result;
218}
219
220static unsigned int pakfire_chrcnt(const char* s, char delim) {
221 size_t length = strlen(s);
222
223 unsigned int count = 0;
224
225 for (unsigned int i = 0; i < length; i++)
226 if (s[i] == delim)
227 count++;
228
229 return count;
230}
231
232char** pakfire_string_split(const char* s, char delim) {
233 char** array = NULL;
234
235 if (!s) {
236 errno = EINVAL;
237 return NULL;
238 }
239
240 // Count how often we need to split
241 unsigned int count = pakfire_chrcnt(s, delim) + 1;
242
243 // Allocate array
244 array = calloc(count + 1, sizeof(*array));
245 if (!array)
246 return NULL;
247
248 // Copy string to stack
249 char* p = strdupa(s);
250 if (!p)
251 return NULL;
252
253 unsigned int i = 0;
254 while (*p) {
255 char* e = strchr(p, delim);
256
257 // Terminate the string
258 if (e)
259 *e = '\0';
260
261 // Add string to the array
262 array[i++] = strdup(p);
263
264 // End loop when we reached the end
265 if (!e)
266 break;
267
268 // Or continue at the next line
269 p = e + 1;
270 }
271
272 return array;
273}
274
275char* pakfire_string_join(char** list, const char* delim) {
276 // Validate input
277 if (!list || !delim) {
278 errno = EINVAL;
279 return NULL;
280 }
281
282 size_t length = 0;
283 unsigned int elements = 0;
284
285 // Count the number of elements and the total length
286 for (char** item = list; *item; item++) {
287 length += strlen(*item);
288 elements++;
289 }
290
291 // Empty list?
292 if (!elements)
293 return NULL;
294
295 // Add the delimiters
296 length += strlen(delim) * (elements - 1);
297
298 // Allocate the result string
299 char* string = malloc(length + 1);
300 if (!string)
301 return NULL;
302
303 // Pointer to where we are writing
304 char* p = string;
305
306 size_t bytes_left = length + 1;
307 size_t bytes_written;
308
309 for (char** item = list; *item; item++) {
310 bytes_written = snprintf(p, bytes_left, "%s", *item);
311
312 bytes_left -= bytes_written;
313 p += bytes_written;
314
315 // Write the delimiter
316 if (bytes_left) {
317 bytes_written = snprintf(p, bytes_left, "%s", delim);
318
319 bytes_left -= bytes_written;
320 p += bytes_written;
321 }
322 }
323
324 return string;
325}
326
327int __pakfire_format_size(char* dst, size_t length, double value) {
328 const char* units[] = {
329 "%.0f ",
330 "%.0fk",
331 "%.1fM",
332 "%.1fG",
333 "%.1fT",
334 NULL
335 };
336 const char** unit = units;
337
338 while (*(unit + 1) && value >= 1024.0) {
339 value /= 1024.0;
340 unit++;
341 }
342
1cd3e864 343 return __pakfire_string_format(dst, length, *unit, value);
d973a13d
MT
344}
345
346int pakfire_format_speed(char* dst, size_t length, double value) {
347 const char* units[] = {
348 "%4.0fB/s",
349 "%4.0fkB/s",
350 "%4.1fMB/s",
351 "%4.1fGB/s",
352 "%4.1fTB/s",
353 NULL
354 };
355 const char** unit = units;
356
357 while (*(unit + 1) && value >= 1024.0) {
358 value /= 1024.0;
359 unit++;
360 }
361
1cd3e864 362 return __pakfire_string_format(dst, length, *unit, value);
d973a13d
MT
363}
364
5cf4a7a1
MT
365int __pakfire_strftime(char* buffer, const size_t length,
366 const char* format, const time_t t) {
d973a13d 367 struct tm* tm = gmtime(&t);
5cf4a7a1 368 int r;
d973a13d 369
5cf4a7a1
MT
370 // Format time
371 r = strftime(buffer, length, format, tm);
372 if (r == 0)
373 return 1;
d973a13d 374
5cf4a7a1 375 return 0;
d973a13d 376}
d973a13d 377
5cf4a7a1 378int __pakfire_strftime_now(char* buffer, size_t length, const char* format) {
d973a13d
MT
379 // Fetch the current time
380 const time_t t = time(NULL);
381 if (t < 0)
382 return 1;
383
5cf4a7a1 384 return __pakfire_strftime(buffer, length, format, t);
d973a13d 385}
528b1f1c 386
b91139b4
MT
387int __pakfire_format_time(char* buffer, const size_t length, const time_t t) {
388 const char* format = NULL;
389
390 // Values smaller than zero for t are invalid
391 if (t < 0)
392 return 1;
393
394 if (t >= 86400)
395 format = "%dd%Hh%Mm";
396 else if (t >= 3600)
7c8cff9a 397 format = "%Hh%Mm%Ss";
b91139b4
MT
398 else if (t >= 60)
399 format = "%Mm%Ss";
400 else
401 format = "%Ss";
402
403 return __pakfire_strftime(buffer, length, format, t);
404}
405
528b1f1c
MT
406size_t pakfire_string_parse_bytes(const char* s) {
407 const char* units = "kMGT";
408 char* unit = NULL;
409
410 // Read the numeric value
411 size_t bytes = strtoul(s, &unit, 10);
412
413 // Return bytes if there is no unit
414 if (!unit || !*unit)
415 return bytes;
416
417 // Is the unit of the correct length?
418 const size_t length = strlen(unit);
419 if (length > 1)
420 goto ERROR;
421
422 // Walk through all units and find a match
423 for (const char* u = units; *u; u++) {
424 bytes *= 1024;
425
426 // End if the unit matches
427 if (*unit == *u)
428 return bytes;
429 }
430
431ERROR:
432 errno = EINVAL;
433 return 0;
434}
8674de39
MT
435
436int pakfire_string_is_url(const char* s) {
437 static const char* known_schemas[] = {
438 "https://",
439 "http://",
440 "file://",
441 NULL,
442 };
443
444 for (const char** schema = known_schemas; *schema; schema++) {
445 if (pakfire_string_startswith(s, *schema))
446 return 1;
447 }
448
449 return 0;
450}