]> git.ipfire.org Git - pakfire.git/blob - src/libpakfire/util.c
filelist: Make parsing more robust
[pakfire.git] / src / libpakfire / util.c
1 /*#############################################################################
2 # #
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 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 <ctype.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <ftw.h>
25 #include <libgen.h>
26 #include <math.h>
27 #include <stddef.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <sys/types.h>
33 #include <time.h>
34 #include <unistd.h>
35
36 #include <archive.h>
37 #include <archive_entry.h>
38 #include <json.h>
39 #include <solv/pool.h>
40 #include <uuid/uuid.h>
41
42 #include <pakfire/constants.h>
43 #include <pakfire/logging.h>
44 #include <pakfire/types.h>
45 #include <pakfire/util.h>
46
47 #define NSEC_PER_SEC 1000000000
48
49 static Id pakfire_parse_namespace(Pool* pool, const char* s) {
50 const char* p = strchr(s, '(');
51 if (!p)
52 return 0;
53
54 // Store the namespace ID
55 Id namespace = pool_strn2id(pool, s, p - s, 1);
56
57 // Find the end of the string
58 s = strrchr(p, ')');
59 if (!s)
60 return 0;
61
62 Id id = pool_strn2id(pool, p + 1, s - p - 1, 1);
63
64 // Bring it all together
65 return pool_rel2id(pool, namespace, id, REL_NAMESPACE, 1);
66 }
67
68 Id pakfire_parse_dep(Pakfire pakfire, const char* s) {
69 Id id;
70
71 if (!s) {
72 errno = EINVAL;
73 return 0;
74 }
75
76 // Ignore empty strings
77 if (!*s)
78 return 0;
79
80 Pool* pool = pakfire_get_solv_pool(pakfire);
81
82 // Consume any leading space
83 if (isspace(*s))
84 s++;
85
86 const char* p = s;
87
88 // Find the first part (before =, >= or <=)
89 while (*p && !isspace(*p) && *p != '<' && *p != '=' && *p != '>')
90 p++;
91
92 // The length of the first part
93 size_t l = p - s;
94
95 // Add name to pool
96 if (pakfire_string_startswith(s, "pakfire("))
97 id = pakfire_parse_namespace(pool, s);
98 else
99 id = pool_strn2id(pool, s, l, 1);
100
101 // Consume any more space
102 if (isspace(*p))
103 p++;
104
105 if (*p == '<' || *p == '=' || *p == '>') {
106 int flags = 0;
107
108 while (1) {
109 if (*p == '<')
110 flags |= REL_LT;
111 else if (*p == '=')
112 flags |= REL_EQ;
113 else if (*p == '>')
114 flags |= REL_GT;
115 else
116 break;
117
118 p++;
119 }
120
121 // Consume any more space
122 if (isspace(*p))
123 p++;
124
125 // Add EVR to pool
126 Id evr = pool_str2id(pool, p, 1);
127
128 // Combine everything
129 id = pool_rel2id(pool, id, evr, flags, 1);
130 }
131
132 return id;
133 }
134
135 void pakfire_parse_deps(Pakfire pakfire, PakfirePackage pkg,
136 void (*func)(PakfirePackage pkg, const char* dep), const char* deps) {
137 char* p = strdupa(deps);
138
139 while (*p) {
140 char* e = strchr(p, '\n');
141
142 // Terminate the string
143 if (e)
144 *e = '\0';
145
146 // Add the dependency
147 func(pkg, p);
148
149 // End loop when we reached the end
150 if (!e)
151 break;
152
153 // Or continue at the next line
154 p = e + 1;
155 }
156 }
157
158 int pakfire_string_startswith(const char* s, const char* prefix) {
159 return !strncmp(s, prefix, strlen(prefix));
160 }
161
162 int pakfire_string_endswith(const char* s, const char* suffix) {
163 return !strcmp(s + strlen(s) - strlen(suffix), suffix);
164 }
165
166 char* pakfire_unquote_in_place(char* s) {
167 if (!s || !*s)
168 return s;
169
170 // Is the first character a quote?
171 if (*s != '"')
172 return s;
173
174 // Find the end of value
175 size_t l = strlen(s);
176 if (!l)
177 return s;
178
179 // Is the last character a quote?
180 if (s[l - 1] != '"')
181 return s;
182
183 // The string seems to be in quotes; remove them
184 s[l - 1] = '\0';
185 s++;
186
187 return s;
188 }
189
190 int pakfire_string_partition(const char* s, const char* delim, char** s1, char** s2) {
191 char* p = strstr(s, delim);
192
193 // Delim was not found
194 if (!p) {
195 *s1 = NULL;
196 *s2 = NULL;
197 return 1;
198 }
199
200 // Length of string before delim
201 size_t l = p - s;
202
203 char* buffer = malloc(l + 1);
204 if (!buffer)
205 return -ENOMEM;
206
207 // Copy first part
208 *s1 = memcpy(buffer, s, l);
209 buffer[l] = '\0';
210
211 // Copy second part
212 *s2 = strdup(p + strlen(delim));
213
214 return 0;
215 }
216
217 char* pakfire_string_replace(const char* s, const char* pattern, const char* repl) {
218 // Return NULL on no input or no pattern
219 if (!s || !pattern) {
220 errno = EINVAL;
221 return NULL;
222 }
223
224 // Replace with nothing when repl is NULL
225 if (!repl)
226 repl = "";
227
228 char* result = NULL;
229 const char** cache = NULL;
230 unsigned int count = 0;
231
232 const size_t pattern_length = strlen(pattern);
233
234 // Working pointer
235 const char* p = s;
236
237 // Find all occurrences of pattern and store their location
238 while (1) {
239 const char* needle = strstr(p, pattern);
240 if (!needle)
241 break;
242
243 // Make space in the cache
244 cache = reallocarray(cache, sizeof(*cache), count + 1);
245 cache[count++] = needle;
246
247 // Move p forward
248 p = needle + pattern_length;
249 }
250
251 // Copy the string if no occurence was found
252 if (count == 0) {
253 result = strdup(s);
254 goto ERROR;
255 }
256
257 // Store the end pointer
258 cache = reallocarray(cache, sizeof(*cache), count + 1);
259 cache[count] = s + strlen(s);
260
261 const size_t repl_length = strlen(repl);
262
263 // Determine the length of the final string
264 const size_t length = strlen(s) + ((repl_length - pattern_length) * count);
265
266 // Allocate enough memory for the result
267 result = malloc(length + 1);
268 if (!result)
269 goto ERROR;
270
271 // Reset p
272 p = s;
273
274 // Working pointer for the result
275 char* r = result;
276
277 // Copy everything up to the first match
278 ssize_t l = cache[0] - s;
279 memcpy(r, p, l);
280 r += l;
281 p += l;
282
283 for (unsigned int i = 0; i < count; i++) {
284 // Put replacement here
285 memcpy(r, repl, repl_length);
286 r += repl_length;
287 p += pattern_length;
288
289 // Determine the length between two matches
290 l = cache[i+1] - (cache[i] + pattern_length);
291
292 memcpy(r, p, l);
293 r += l;
294 p += l;
295 }
296
297 // Terminate the string
298 result[length] = '\0';
299
300 ERROR:
301 if (cache)
302 free(cache);
303
304 return result;
305 }
306
307 static unsigned int pakfire_chrcnt(const char* s, char delim) {
308 size_t length = strlen(s);
309
310 unsigned int count = 0;
311
312 for (unsigned int i = 0; i < length; i++)
313 if (s[i] == delim)
314 count++;
315
316 return count;
317 }
318
319 char** pakfire_split_string(const char* s, char delim) {
320 char** array = NULL;
321
322 if (!s) {
323 errno = EINVAL;
324 return NULL;
325 }
326
327 // Count how often we need to split
328 unsigned int count = pakfire_chrcnt(s, delim) + 1;
329
330 // Allocate array
331 array = calloc(count + 1, sizeof(*array));
332 if (!array)
333 return NULL;
334
335 // Copy string to stack
336 char* p = strdupa(s);
337 if (!p)
338 return NULL;
339
340 unsigned int i = 0;
341 while (*p) {
342 char* e = strchr(p, delim);
343
344 // Terminate the string
345 if (e)
346 *e = '\0';
347
348 // Add string to the array
349 array[i++] = strdup(p);
350
351 // End loop when we reached the end
352 if (!e)
353 break;
354
355 // Or continue at the next line
356 p = e + 1;
357 }
358
359 return array;
360 }
361
362 char* pakfire_string_join(char** list, const char* delim) {
363 if (!list)
364 return NULL;
365
366 size_t length = 0;
367 unsigned int elements = 0;
368
369 // Count the number of elements and the total length
370 for (char** item = list; *item; item++) {
371 length += strlen(*item);
372 elements++;
373 }
374
375 // Empty list?
376 if (!elements)
377 return NULL;
378
379 // Add the delimiters
380 length += strlen(delim) * (elements - 1);
381
382 // Allocate the result string
383 char* string = malloc(length + 1);
384 if (!string)
385 return NULL;
386
387 // Pointer to where we are writing
388 char* p = string;
389
390 size_t bytes_left = length;
391 size_t bytes_written;
392
393 for (char** item = list; *item; item++) {
394 bytes_written = snprintf(p, bytes_left, "%s", *item);
395
396 bytes_left -= bytes_written;
397 p += bytes_written;
398
399 // Write the delimiter
400 if (bytes_left) {
401 bytes_written = snprintf(p, bytes_left, "%s", delim);
402
403 bytes_left -= bytes_written;
404 p += bytes_written;
405 }
406 }
407
408 return string;
409 }
410
411 int pakfire_format_size(char* dst, size_t length, double value) {
412 const char* units[] = {
413 "%.0f ",
414 "%.0fk",
415 "%.1fM",
416 "%.1fG",
417 "%.1fT",
418 NULL
419 };
420 const char** unit = units;
421
422 while (*(unit + 1) && value >= 1024.0) {
423 value /= 1024.0;
424 unit++;
425 }
426
427 #pragma GCC diagnostic push
428 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
429 return snprintf(dst, length, *unit, value);
430 #pragma GCC diagnostic pop
431 }
432
433 int pakfire_format_speed(char* dst, size_t length, double value) {
434 const char* units[] = {
435 "%4.0fB/s",
436 "%4.0fkB/s",
437 "%4.1fMB/s",
438 "%4.1fGB/s",
439 "%4.1fTB/s",
440 NULL
441 };
442 const char** unit = units;
443
444 while (*(unit + 1) && value >= 1024.0) {
445 value /= 1024.0;
446 unit++;
447 }
448
449 #pragma GCC diagnostic push
450 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
451 return snprintf(dst, length, *unit, value);
452 #pragma GCC diagnostic pop
453 }
454
455 #pragma GCC diagnostic push
456 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
457 static char* pakfire_strftime(const char* format, time_t t) {
458 char string[128];
459 struct tm* tm = gmtime(&t);
460
461 strftime(string, sizeof(string) - 1, format, tm);
462
463 return strdup(string);
464 }
465 #pragma GCC diagnostic pop
466
467 char* pakfire_format_date(time_t t) {
468 return pakfire_strftime("%Y-%m-%d", t);
469 }
470
471 int __pakfire_path_join(char* dest, size_t length,
472 const char* first, const char* second) {
473 if (!first)
474 return snprintf(dest, length, "%s", second);
475
476 if (!second)
477 return snprintf(dest, length, "%s", first);
478
479 // Remove leading slashes from second argument
480 while (*second == '/')
481 second++;
482
483 return snprintf(dest, length, "%s/%s", first, second);
484 }
485
486 const char* pakfire_path_relpath(const char* root, const char* path) {
487 if (pakfire_string_startswith(path, root))
488 return path + strlen(root);
489
490 return NULL;
491 }
492
493 int pakfire_path_exists(const char* path) {
494 return !access(path, F_OK);
495 }
496
497 time_t pakfire_path_age(const char* path) {
498 struct stat st;
499
500 int r = stat(path, &st);
501 if (r == 0) {
502 // Get current timestamp
503 time_t now = time(NULL);
504
505 // Return the difference since the file has been created and now
506 return now - st.st_ctime;
507 }
508
509 return -1;
510 }
511
512 char* pakfire_basename(const char* path) {
513 char* name = strdup(path);
514
515 char* r = basename(name);
516 if (r)
517 r = strdup(r);
518
519 free(name);
520
521 return r;
522 }
523
524 char* pakfire_dirname(const char* path) {
525 char* parent = strdup(path);
526
527 char* r = dirname(parent);
528 if (r)
529 r = strdup(r);
530
531 free(parent);
532
533 return r;
534 }
535
536 char* pakfire_remove_trailing_newline(char* str) {
537 ssize_t pos = strlen(str) - 1;
538
539 if (str[pos] == '\n')
540 str[pos] = '\0';
541
542 return str;
543 }
544
545 int pakfire_read_file_into_buffer(FILE* f, char** buffer, size_t* len) {
546 if (!f)
547 return -EBADF;
548
549 int r = fseek(f, 0, SEEK_END);
550 if (r)
551 return r;
552
553 // Save length of file
554 *len = ftell(f);
555
556 // Go back to the start
557 r = fseek(f, 0, SEEK_SET);
558 if (r)
559 return r;
560
561 // Allocate buffer
562 *buffer = malloc((sizeof(**buffer) * *len) + 1);
563 if (!*buffer)
564 return -ENOMEM;
565
566 // Read content
567 fread(*buffer, *len, sizeof(**buffer), f);
568
569 // Check we encountered any errors
570 r = ferror(f);
571 if (r) {
572 free(*buffer);
573 return r;
574 }
575
576 // Terminate the buffer
577 (*buffer)[*len] = '\0';
578
579 return 0;
580 }
581
582 char* pakfire_generate_uuid() {
583 uuid_t uuid;
584
585 // Generate a new random value
586 uuid_generate_random(uuid);
587
588 char* ret = malloc(UUID_STR_LEN + 1);
589 if (!ret)
590 return NULL;
591
592 // Convert it to string
593 uuid_unparse_lower(uuid, ret);
594
595 // Terminate string
596 ret[UUID_STR_LEN] = '\0';
597
598 return ret;
599 }
600
601 char* pakfire_hexlify(const char* digest, const size_t length) {
602 const char* hexdigits = "0123456789abcdef";
603
604 char* s = malloc((length * 2) + 1);
605 if (!s)
606 return NULL;
607
608 for (unsigned int i = 0, j = 0; i < length; i++) {
609 char b = digest[i];
610
611 s[j++] = hexdigits[(b >> 4) & 0xf];
612 s[j++] = hexdigits[(b & 0x0f)];
613 }
614
615 // Terminate result
616 s[length * 2] = '\0';
617
618 return s;
619 }
620
621 static int pakfire_mkparentdir(const char* path, mode_t mode) {
622 int r;
623
624 char* dirname = pakfire_dirname(path);
625 if (!dirname)
626 return 1;
627
628 // We have arrived at the top of the tree
629 if (*dirname == '.' || strcmp(dirname, "/") == 0)
630 return 0;
631
632 // Ensure the parent directory exists
633 r = pakfire_mkparentdir(dirname, mode);
634 if (r)
635 goto END;
636
637 // Create this directory
638 r = mkdir(dirname, mode);
639
640 // Ignore when the directory already exists
641 if (r && errno == EEXIST)
642 r = 0;
643
644 END:
645 free(dirname);
646
647 return r;
648 }
649
650 int pakfire_mkdir(const char* path, mode_t mode) {
651 int r = pakfire_mkparentdir(path, mode);
652 if (r)
653 return r;
654
655 // Finally, create the directory we want
656 return mkdir(path, mode);
657 }
658
659 FILE* pakfire_mktemp(char* path) {
660 int r = pakfire_mkparentdir(path, 0);
661 if (r)
662 return NULL;
663
664 // Create a temporary result file
665 int fd = mkostemp(path, O_CLOEXEC);
666 if (fd < 0)
667 return NULL;
668
669 // Re-open as file handle
670 return fdopen(fd, "w+");
671 }
672
673 char* pakfire_mkdtemp(char* path) {
674 int r = pakfire_mkparentdir(path, 0);
675 if (r)
676 return NULL;
677
678 return mkdtemp(path);
679 }
680
681 static int _unlink(const char* path, const struct stat* stat, int typeflag, struct FTW* ftwbuf) {
682 return remove(path);
683 }
684
685 int pakfire_rmtree(const char* path, int flags) {
686 int r = nftw(path, _unlink, 64, flags|FTW_DEPTH|FTW_PHYS);
687
688 // Ignore if path didn't exist
689 if (r < 0 && errno == ENOENT)
690 r = 0;
691
692 return r;
693 }
694
695 // Archive Stuff
696
697 int pakfire_archive_copy_data(struct archive* src, struct archive* dst,
698 struct archive_entry* entry) {
699 const void* buffer;
700 size_t size;
701 off_t offset;
702 int r;
703
704 for (;;) {
705 // Read a block of data
706 r = archive_read_data_block(src, &buffer, &size, &offset);
707 if (r == ARCHIVE_EOF)
708 return ARCHIVE_OK;
709 else if (r)
710 return r;
711
712 // Write the read block of data
713 r = archive_write_data(dst, buffer, size);
714 if (r < 0)
715 return r;
716 }
717 }
718
719 int pakfire_archive_copy_data_to_buffer(Pakfire pakfire, struct archive* a,
720 struct archive_entry* entry, char** data, size_t* data_size) {
721 *data = NULL;
722 *data_size = 0;
723
724 size_t required_size = archive_entry_size(entry);
725 if (!required_size)
726 return 0;
727
728 // Allocate a block of the required size
729 *data = calloc(1, required_size + 1);
730 if (!*data)
731 return ENOMEM;
732
733 ssize_t bytes_read = archive_read_data(a, *data, required_size);
734 if (bytes_read < 0) {
735 ERROR(pakfire, "Could not read from archive: %s\n", archive_error_string(a));
736 free(*data);
737 return 1;
738 }
739
740 *data_size = bytes_read;
741
742 return 0;
743 }
744
745 // JSON Stuff
746
747 static struct json_object* pakfire_json_parse(Pakfire pakfire, FILE* f) {
748 struct json_tokener* tokener = NULL;
749 struct json_object* json = NULL;
750 char* buffer = NULL;
751 size_t length;
752
753 // Read everything into memory
754 int r = pakfire_read_file_into_buffer(f, &buffer, &length);
755 if (r)
756 goto ERROR;
757
758 // Create tokener
759 tokener = json_tokener_new();
760 if (!tokener) {
761 ERROR(pakfire, "Could not allocate JSON tokener: %s\n", strerror(errno));
762 goto ERROR;
763 }
764
765 // Parse JSON from path
766 json = json_tokener_parse_ex(tokener, buffer, length);
767 if (!json) {
768 enum json_tokener_error error = json_tokener_get_error(tokener);
769
770 ERROR(pakfire, "JSON parsing error: %s\n", json_tokener_error_desc(error));
771 goto ERROR;
772 }
773
774 // Log what we have parsed
775 DEBUG(pakfire, "Parsed JSON:\n%s\n",
776 json_object_to_json_string_ext(json,
777 JSON_C_TO_STRING_SPACED | JSON_C_TO_STRING_PRETTY)
778 );
779
780 ERROR:
781 if (buffer)
782 free(buffer);
783
784 if (tokener)
785 json_tokener_free(tokener);
786
787 return json;
788 }
789
790 struct json_object* pakfire_json_parse_from_file(Pakfire pakfire, const char* path) {
791 FILE* f = fopen(path, "r");
792 if (!f)
793 return NULL;
794
795 struct json_object* json = pakfire_json_parse(pakfire, f);
796 fclose(f);
797
798 return json;
799 }
800
801 // Time Stuff
802
803 static void timespec_normalize(struct timespec* t) {
804 while (t->tv_nsec >= NSEC_PER_SEC) {
805 t->tv_sec++;
806 t->tv_nsec -= NSEC_PER_SEC;
807 }
808
809 while (t->tv_nsec <= -NSEC_PER_SEC) {
810 t->tv_sec--;
811 t->tv_nsec += NSEC_PER_SEC;
812 }
813 }
814
815 struct timespec timespec_add(const struct timespec* t1, const struct timespec* t2) {
816 struct timespec r = {
817 .tv_sec = t1->tv_sec + t2->tv_sec,
818 .tv_nsec = t2->tv_nsec + t2->tv_nsec,
819 };
820
821 // Correct any negative values
822 timespec_normalize(&r);
823
824 return r;
825 }
826
827 struct timespec timespec_from_ms(int milliseconds) {
828 struct timespec t = {
829 .tv_sec = (milliseconds / 1000),
830 .tv_nsec = (milliseconds % 1000) * 1000000,
831 };
832
833 return t;
834 }
835
836 int timespec_lt(struct timespec* t1, struct timespec* t2) {
837 return (
838 t1->tv_sec < t2->tv_sec ||
839 (t1->tv_sec == t2->tv_sec && t1->tv_nsec < t2->tv_nsec)
840 );
841 }