]>
git.ipfire.org Git - pakfire.git/blob - src/libpakfire/util.c
1 /*#############################################################################
3 # Pakfire - The IPFire package management system #
4 # Copyright (C) 2013 Pakfire development team #
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. #
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. #
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/>. #
19 #############################################################################*/
32 #include <sys/types.h>
37 #include <archive_entry.h>
39 #include <solv/pool.h>
40 #include <uuid/uuid.h>
42 #include <pakfire/constants.h>
43 #include <pakfire/logging.h>
44 #include <pakfire/types.h>
45 #include <pakfire/util.h>
47 #define NSEC_PER_SEC 1000000000
49 static Id
pakfire_parse_namespace(Pool
* pool
, const char* s
) {
50 const char* p
= strchr(s
, '(');
54 // Store the namespace ID
55 Id
namespace = pool_strn2id(pool
, s
, p
- s
, 1);
57 // Find the end of the string
62 Id id
= pool_strn2id(pool
, p
+ 1, s
- p
- 1, 1);
64 // Bring it all together
65 return pool_rel2id(pool
, namespace, id
, REL_NAMESPACE
, 1);
68 Id
pakfire_parse_dep(Pakfire pakfire
, const char* s
) {
76 // Ignore empty strings
80 Pool
* pool
= pakfire_get_solv_pool(pakfire
);
82 // Consume any leading space
88 // Find the first part (before =, >= or <=)
89 while (*p
&& !isspace(*p
) && *p
!= '<' && *p
!= '=' && *p
!= '>')
92 // The length of the first part
96 if (pakfire_string_startswith(s
, "pakfire("))
97 id
= pakfire_parse_namespace(pool
, s
);
99 id
= pool_strn2id(pool
, s
, l
, 1);
101 // Consume any more space
105 if (*p
== '<' || *p
== '=' || *p
== '>') {
121 // Consume any more space
126 Id evr
= pool_str2id(pool
, p
, 1);
128 // Combine everything
129 id
= pool_rel2id(pool
, id
, evr
, flags
, 1);
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
);
140 char* e
= strchr(p
, '\n');
142 // Terminate the string
146 // Add the dependency
149 // End loop when we reached the end
153 // Or continue at the next line
158 int pakfire_string_startswith(const char* s
, const char* prefix
) {
159 return !strncmp(s
, prefix
, strlen(prefix
));
162 int pakfire_string_endswith(const char* s
, const char* suffix
) {
163 return !strcmp(s
+ strlen(s
) - strlen(suffix
), suffix
);
166 char* pakfire_unquote_in_place(char* s
) {
170 // Is the first character a quote?
174 // Find the end of value
175 size_t l
= strlen(s
);
179 // Is the last character a quote?
183 // The string seems to be in quotes; remove them
190 int pakfire_string_partition(const char* s
, const char* delim
, char** s1
, char** s2
) {
191 char* p
= strstr(s
, delim
);
193 // Delim was not found
200 // Length of string before delim
203 char* buffer
= malloc(l
+ 1);
208 *s1
= memcpy(buffer
, s
, l
);
212 *s2
= strdup(p
+ strlen(delim
));
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
) {
224 // Replace with nothing when repl is NULL
229 const char** cache
= NULL
;
230 unsigned int count
= 0;
232 const size_t pattern_length
= strlen(pattern
);
237 // Find all occurrences of pattern and store their location
239 const char* needle
= strstr(p
, pattern
);
243 // Make space in the cache
244 cache
= reallocarray(cache
, sizeof(*cache
), count
+ 1);
245 cache
[count
++] = needle
;
248 p
= needle
+ pattern_length
;
251 // Copy the string if no occurence was found
257 // Store the end pointer
258 cache
= reallocarray(cache
, sizeof(*cache
), count
+ 1);
259 cache
[count
] = s
+ strlen(s
);
261 const size_t repl_length
= strlen(repl
);
263 // Determine the length of the final string
264 const size_t length
= strlen(s
) + ((repl_length
- pattern_length
) * count
);
266 // Allocate enough memory for the result
267 result
= malloc(length
+ 1);
274 // Working pointer for the result
277 // Copy everything up to the first match
278 ssize_t l
= cache
[0] - s
;
283 for (unsigned int i
= 0; i
< count
; i
++) {
284 // Put replacement here
285 memcpy(r
, repl
, repl_length
);
289 // Determine the length between two matches
290 l
= cache
[i
+1] - (cache
[i
] + pattern_length
);
297 // Terminate the string
298 result
[length
] = '\0';
307 static unsigned int pakfire_chrcnt(const char* s
, char delim
) {
308 size_t length
= strlen(s
);
310 unsigned int count
= 0;
312 for (unsigned int i
= 0; i
< length
; i
++)
319 char** pakfire_split_string(const char* s
, char delim
) {
327 // Count how often we need to split
328 unsigned int count
= pakfire_chrcnt(s
, delim
) + 1;
331 array
= calloc(count
+ 1, sizeof(*array
));
335 // Copy string to stack
336 char* p
= strdupa(s
);
342 char* e
= strchr(p
, delim
);
344 // Terminate the string
348 // Add string to the array
349 array
[i
++] = strdup(p
);
351 // End loop when we reached the end
355 // Or continue at the next line
362 char* pakfire_string_join(char** list
, const char* delim
) {
367 unsigned int elements
= 0;
369 // Count the number of elements and the total length
370 for (char** item
= list
; *item
; item
++) {
371 length
+= strlen(*item
);
379 // Add the delimiters
380 length
+= strlen(delim
) * (elements
- 1);
382 // Allocate the result string
383 char* string
= malloc(length
+ 1);
387 // Pointer to where we are writing
390 size_t bytes_left
= length
;
391 size_t bytes_written
;
393 for (char** item
= list
; *item
; item
++) {
394 bytes_written
= snprintf(p
, bytes_left
, "%s", *item
);
396 bytes_left
-= bytes_written
;
399 // Write the delimiter
401 bytes_written
= snprintf(p
, bytes_left
, "%s", delim
);
403 bytes_left
-= bytes_written
;
411 int pakfire_format_size(char* dst
, size_t length
, double value
) {
412 const char* units
[] = {
420 const char** unit
= units
;
422 while (*(unit
+ 1) && value
>= 1024.0) {
427 #pragma GCC diagnostic push
428 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
429 return snprintf(dst
, length
, *unit
, value
);
430 #pragma GCC diagnostic pop
433 int pakfire_format_speed(char* dst
, size_t length
, double value
) {
434 const char* units
[] = {
442 const char** unit
= units
;
444 while (*(unit
+ 1) && value
>= 1024.0) {
449 #pragma GCC diagnostic push
450 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
451 return snprintf(dst
, length
, *unit
, value
);
452 #pragma GCC diagnostic pop
455 #pragma GCC diagnostic push
456 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
457 static char* pakfire_strftime(const char* format
, time_t t
) {
459 struct tm
* tm
= gmtime(&t
);
461 strftime(string
, sizeof(string
) - 1, format
, tm
);
463 return strdup(string
);
465 #pragma GCC diagnostic pop
467 char* pakfire_format_date(time_t t
) {
468 return pakfire_strftime("%Y-%m-%d", t
);
471 int __pakfire_path_join(char* dest
, size_t length
,
472 const char* first
, const char* second
) {
474 return snprintf(dest
, length
, "%s", second
);
477 return snprintf(dest
, length
, "%s", first
);
479 // Remove leading slashes from second argument
480 while (*second
== '/')
483 return snprintf(dest
, length
, "%s/%s", first
, second
);
486 const char* pakfire_path_relpath(const char* root
, const char* path
) {
487 if (pakfire_string_startswith(path
, root
))
488 return path
+ strlen(root
);
493 int pakfire_path_exists(const char* path
) {
494 return !access(path
, F_OK
);
497 time_t pakfire_path_age(const char* path
) {
500 int r
= stat(path
, &st
);
502 // Get current timestamp
503 time_t now
= time(NULL
);
505 // Return the difference since the file has been created and now
506 return now
- st
.st_ctime
;
512 char* pakfire_basename(const char* path
) {
513 char* name
= strdup(path
);
515 char* r
= basename(name
);
524 char* pakfire_dirname(const char* path
) {
525 char* parent
= strdup(path
);
527 char* r
= dirname(parent
);
536 char* pakfire_remove_trailing_newline(char* str
) {
537 ssize_t pos
= strlen(str
) - 1;
539 if (str
[pos
] == '\n')
545 int pakfire_read_file_into_buffer(FILE* f
, char** buffer
, size_t* len
) {
549 int r
= fseek(f
, 0, SEEK_END
);
553 // Save length of file
556 // Go back to the start
557 r
= fseek(f
, 0, SEEK_SET
);
562 *buffer
= malloc((sizeof(**buffer
) * *len
) + 1);
567 fread(*buffer
, *len
, sizeof(**buffer
), f
);
569 // Check we encountered any errors
576 // Terminate the buffer
577 (*buffer
)[*len
] = '\0';
582 char* pakfire_generate_uuid() {
585 // Generate a new random value
586 uuid_generate_random(uuid
);
588 char* ret
= malloc(UUID_STR_LEN
+ 1);
592 // Convert it to string
593 uuid_unparse_lower(uuid
, ret
);
596 ret
[UUID_STR_LEN
] = '\0';
601 char* pakfire_hexlify(const char* digest
, const size_t length
) {
602 const char* hexdigits
= "0123456789abcdef";
604 char* s
= malloc((length
* 2) + 1);
608 for (unsigned int i
= 0, j
= 0; i
< length
; i
++) {
611 s
[j
++] = hexdigits
[(b
>> 4) & 0xf];
612 s
[j
++] = hexdigits
[(b
& 0x0f)];
616 s
[length
* 2] = '\0';
621 static int pakfire_mkparentdir(const char* path
, mode_t mode
) {
624 char* dirname
= pakfire_dirname(path
);
628 // We have arrived at the top of the tree
629 if (*dirname
== '.' || strcmp(dirname
, "/") == 0)
632 // Ensure the parent directory exists
633 r
= pakfire_mkparentdir(dirname
, mode
);
637 // Create this directory
638 r
= mkdir(dirname
, mode
);
640 // Ignore when the directory already exists
641 if (r
&& errno
== EEXIST
)
650 int pakfire_mkdir(const char* path
, mode_t mode
) {
651 int r
= pakfire_mkparentdir(path
, mode
);
655 // Finally, create the directory we want
656 return mkdir(path
, mode
);
659 FILE* pakfire_mktemp(char* path
) {
660 int r
= pakfire_mkparentdir(path
, 0);
664 // Create a temporary result file
665 int fd
= mkostemp(path
, O_CLOEXEC
);
669 // Re-open as file handle
670 return fdopen(fd
, "w+");
673 char* pakfire_mkdtemp(char* path
) {
674 int r
= pakfire_mkparentdir(path
, 0);
678 return mkdtemp(path
);
681 static int _unlink(const char* path
, const struct stat
* stat
, int typeflag
, struct FTW
* ftwbuf
) {
685 int pakfire_rmtree(const char* path
, int flags
) {
686 int r
= nftw(path
, _unlink
, 64, flags
|FTW_DEPTH
|FTW_PHYS
);
688 // Ignore if path didn't exist
689 if (r
< 0 && errno
== ENOENT
)
697 int pakfire_archive_copy_data(struct archive
* src
, struct archive
* dst
,
698 struct archive_entry
* entry
) {
705 // Read a block of data
706 r
= archive_read_data_block(src
, &buffer
, &size
, &offset
);
707 if (r
== ARCHIVE_EOF
)
712 // Write the read block of data
713 r
= archive_write_data(dst
, buffer
, size
);
719 int pakfire_archive_copy_data_to_buffer(Pakfire pakfire
, struct archive
* a
,
720 struct archive_entry
* entry
, char** data
, size_t* data_size
) {
724 size_t required_size
= archive_entry_size(entry
);
728 // Allocate a block of the required size
729 *data
= calloc(1, required_size
+ 1);
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
));
740 *data_size
= bytes_read
;
747 static struct json_object
* pakfire_json_parse(Pakfire pakfire
, FILE* f
) {
748 struct json_tokener
* tokener
= NULL
;
749 struct json_object
* json
= NULL
;
753 // Read everything into memory
754 int r
= pakfire_read_file_into_buffer(f
, &buffer
, &length
);
759 tokener
= json_tokener_new();
761 ERROR(pakfire
, "Could not allocate JSON tokener: %s\n", strerror(errno
));
765 // Parse JSON from path
766 json
= json_tokener_parse_ex(tokener
, buffer
, length
);
768 enum json_tokener_error error
= json_tokener_get_error(tokener
);
770 ERROR(pakfire
, "JSON parsing error: %s\n", json_tokener_error_desc(error
));
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
)
785 json_tokener_free(tokener
);
790 struct json_object
* pakfire_json_parse_from_file(Pakfire pakfire
, const char* path
) {
791 FILE* f
= fopen(path
, "r");
795 struct json_object
* json
= pakfire_json_parse(pakfire
, f
);
803 static void timespec_normalize(struct timespec
* t
) {
804 while (t
->tv_nsec
>= NSEC_PER_SEC
) {
806 t
->tv_nsec
-= NSEC_PER_SEC
;
809 while (t
->tv_nsec
<= -NSEC_PER_SEC
) {
811 t
->tv_nsec
+= NSEC_PER_SEC
;
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
,
821 // Correct any negative values
822 timespec_normalize(&r
);
827 struct timespec
timespec_from_ms(int milliseconds
) {
828 struct timespec t
= {
829 .tv_sec
= (milliseconds
/ 1000),
830 .tv_nsec
= (milliseconds
% 1000) * 1000000,
836 int timespec_lt(struct timespec
* t1
, struct timespec
* t2
) {
838 t1
->tv_sec
< t2
->tv_sec
||
839 (t1
->tv_sec
== t2
->tv_sec
&& t1
->tv_nsec
< t2
->tv_nsec
)