]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/string.c
file: Log an error message when setting path fails
[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
d973a13d
MT
60int 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
70int 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
80int 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
90int 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
117char* 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
200ERROR:
201 if (cache)
202 free(cache);
203
204 return result;
205}
206
207static 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
219char** 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
262char* 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
314int __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
333int 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
352int __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 365int __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
374size_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
399ERROR:
400 errno = EINVAL;
401 return 0;
402}