]>
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; | |
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 |
52 | int 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 | ||
62 | int 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 | ||
72 | int 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 | ||
82 | int 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 | ||
109 | char* 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 | ||
192 | ERROR: | |
193 | if (cache) | |
194 | free(cache); | |
195 | ||
196 | return result; | |
197 | } | |
198 | ||
199 | static 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 | ||
211 | char** 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 | ||
254 | char* 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 | ||
306 | int __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 | ||
328 | int 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" | |
352 | static 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 | ||
362 | char* pakfire_format_date(time_t t) { | |
363 | return pakfire_strftime("%Y-%m-%d", t); | |
364 | } | |
365 | ||
366 | int __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 | } |