]> git.ipfire.org Git - pakfire.git/blame - src/libpakfire/string.c
util: Refactor pakfire_path_join
[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;
32
33 // Write string to buffer
34 va_start(args, format);
35 const ssize_t required = vsnprintf(s, length, format, args);
36 va_end(args);
37
38 // Catch any errors
39 if (required < 0)
40 return 1;
41
42 // Check if the entire string could be written
43 if ((size_t)required >= length) {
44 errno = ENOMEM;
45 return 1;
46 }
47
48 // Success
49 return 0;
50}
51
d973a13d
MT
52int pakfire_string_startswith(const char* s, const char* prefix) {
53 // Validate input
54 if (!s || !prefix) {
55 errno = EINVAL;
56 return 1;
57 }
58
59 return !strncmp(s, prefix, strlen(prefix));
60}
61
62int pakfire_string_endswith(const char* s, const char* suffix) {
63 // Validate input
64 if (!s || !suffix) {
65 errno = EINVAL;
66 return 1;
67 }
68
69 return !strcmp(s + strlen(s) - strlen(suffix), suffix);
70}
71
72int pakfire_string_matches(const char* s, const char* pattern) {
73 // Validate input
74 if (!s || !pattern) {
75 errno = EINVAL;
76 return 1;
77 }
78
79 return !!strstr(s, pattern);
80}
81
82int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) {
83 char* p = strstr(s, delim);
84
85 // Delim was not found
86 if (!p) {
87 *s1 = NULL;
88 *s2 = NULL;
89 return 1;
90 }
91
92 // Length of string before delim
93 size_t l = p - s;
94
95 char* buffer = malloc(l + 1);
96 if (!buffer)
97 return 1;
98
99 // Copy first part
100 *s1 = memcpy(buffer, s, l);
101 buffer[l] = '\0';
102
103 // Copy second part
104 *s2 = strdup(p + strlen(delim));
105
106 return 0;
107}
108
109char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) {
110 // Return NULL on no input or no pattern
111 if (!s || !pattern) {
112 errno = EINVAL;
113 return NULL;
114 }
115
116 // Replace with nothing when repl is NULL
117 if (!repl)
118 repl = "";
119
120 char* result = NULL;
121 const char** cache = NULL;
122 unsigned int count = 0;
123
124 const size_t pattern_length = strlen(pattern);
125
126 // Working pointer
127 const char* p = s;
128
129 // Find all occurrences of pattern and store their location
130 while (1) {
131 const char* needle = strstr(p, pattern);
132 if (!needle)
133 break;
134
135 // Make space in the cache
136 cache = reallocarray(cache, sizeof(*cache), count + 1);
137 cache[count++] = needle;
138
139 // Move p forward
140 p = needle + pattern_length;
141 }
142
143 // Copy the string if no occurence was found
144 if (count == 0) {
145 result = strdup(s);
146 goto ERROR;
147 }
148
149 // Store the end pointer
150 cache = reallocarray(cache, sizeof(*cache), count + 1);
151 cache[count] = s + strlen(s);
152
153 const size_t repl_length = strlen(repl);
154
155 // Determine the length of the final string
156 const size_t length = strlen(s) + ((repl_length - pattern_length) * count);
157
158 // Allocate enough memory for the result
159 result = malloc(length + 1);
160 if (!result)
161 goto ERROR;
162
163 // Reset p
164 p = s;
165
166 // Working pointer for the result
167 char* r = result;
168
169 // Copy everything up to the first match
170 ssize_t l = cache[0] - s;
171 memcpy(r, p, l);
172 r += l;
173 p += l;
174
175 for (unsigned int i = 0; i < count; i++) {
176 // Put replacement here
177 memcpy(r, repl, repl_length);
178 r += repl_length;
179 p += pattern_length;
180
181 // Determine the length between two matches
182 l = cache[i+1] - (cache[i] + pattern_length);
183
184 memcpy(r, p, l);
185 r += l;
186 p += l;
187 }
188
189 // Terminate the string
190 result[length] = '\0';
191
192ERROR:
193 if (cache)
194 free(cache);
195
196 return result;
197}
198
199static unsigned int pakfire_chrcnt(const char* s, char delim) {
200 size_t length = strlen(s);
201
202 unsigned int count = 0;
203
204 for (unsigned int i = 0; i < length; i++)
205 if (s[i] == delim)
206 count++;
207
208 return count;
209}
210
211char** pakfire_string_split(const char* s, char delim) {
212 char** array = NULL;
213
214 if (!s) {
215 errno = EINVAL;
216 return NULL;
217 }
218
219 // Count how often we need to split
220 unsigned int count = pakfire_chrcnt(s, delim) + 1;
221
222 // Allocate array
223 array = calloc(count + 1, sizeof(*array));
224 if (!array)
225 return NULL;
226
227 // Copy string to stack
228 char* p = strdupa(s);
229 if (!p)
230 return NULL;
231
232 unsigned int i = 0;
233 while (*p) {
234 char* e = strchr(p, delim);
235
236 // Terminate the string
237 if (e)
238 *e = '\0';
239
240 // Add string to the array
241 array[i++] = strdup(p);
242
243 // End loop when we reached the end
244 if (!e)
245 break;
246
247 // Or continue at the next line
248 p = e + 1;
249 }
250
251 return array;
252}
253
254char* pakfire_string_join(char** list, const char* delim) {
255 // Validate input
256 if (!list || !delim) {
257 errno = EINVAL;
258 return NULL;
259 }
260
261 size_t length = 0;
262 unsigned int elements = 0;
263
264 // Count the number of elements and the total length
265 for (char** item = list; *item; item++) {
266 length += strlen(*item);
267 elements++;
268 }
269
270 // Empty list?
271 if (!elements)
272 return NULL;
273
274 // Add the delimiters
275 length += strlen(delim) * (elements - 1);
276
277 // Allocate the result string
278 char* string = malloc(length + 1);
279 if (!string)
280 return NULL;
281
282 // Pointer to where we are writing
283 char* p = string;
284
285 size_t bytes_left = length + 1;
286 size_t bytes_written;
287
288 for (char** item = list; *item; item++) {
289 bytes_written = snprintf(p, bytes_left, "%s", *item);
290
291 bytes_left -= bytes_written;
292 p += bytes_written;
293
294 // Write the delimiter
295 if (bytes_left) {
296 bytes_written = snprintf(p, bytes_left, "%s", delim);
297
298 bytes_left -= bytes_written;
299 p += bytes_written;
300 }
301 }
302
303 return string;
304}
305
306int __pakfire_format_size(char* dst, size_t length, double value) {
307 const char* units[] = {
308 "%.0f ",
309 "%.0fk",
310 "%.1fM",
311 "%.1fG",
312 "%.1fT",
313 NULL
314 };
315 const char** unit = units;
316
317 while (*(unit + 1) && value >= 1024.0) {
318 value /= 1024.0;
319 unit++;
320 }
321
322#pragma GCC diagnostic push
323#pragma GCC diagnostic ignored "-Wformat-nonliteral"
324 return snprintf(dst, length, *unit, value);
325#pragma GCC diagnostic pop
326}
327
328int pakfire_format_speed(char* dst, size_t length, double value) {
329 const char* units[] = {
330 "%4.0fB/s",
331 "%4.0fkB/s",
332 "%4.1fMB/s",
333 "%4.1fGB/s",
334 "%4.1fTB/s",
335 NULL
336 };
337 const char** unit = units;
338
339 while (*(unit + 1) && value >= 1024.0) {
340 value /= 1024.0;
341 unit++;
342 }
343
344#pragma GCC diagnostic push
345#pragma GCC diagnostic ignored "-Wformat-nonliteral"
346 return snprintf(dst, length, *unit, value);
347#pragma GCC diagnostic pop
348}
349
350#pragma GCC diagnostic push
351#pragma GCC diagnostic ignored "-Wformat-nonliteral"
352static char* pakfire_strftime(const char* format, time_t t) {
353 char string[128];
354 struct tm* tm = gmtime(&t);
355
356 strftime(string, sizeof(string) - 1, format, tm);
357
358 return strdup(string);
359}
360#pragma GCC diagnostic pop
361
362char* pakfire_format_date(time_t t) {
363 return pakfire_strftime("%Y-%m-%d", t);
364}
365
366int __pakfire_strftime_now(char* dest, size_t length, const char* format) {
367 struct tm tm;
368
369 // Fetch the current time
370 const time_t t = time(NULL);
371 if (t < 0)
372 return 1;
373
374 // Convert to struct tm
375 struct tm* now = gmtime_r(&t, &tm);
376 if (!now)
377 return 1;
378
379#pragma GCC diagnostic push
380#pragma GCC diagnostic ignored "-Wformat-nonliteral"
381 strftime(dest, length, format, now);
382#pragma GCC diagnostic pop
383
384 return 0;
385}