Refactor package module into C #6
|
|
@ -4,9 +4,9 @@
|
||||||
typedef struct vieter_package vieter_package;
|
typedef struct vieter_package vieter_package;
|
||||||
|
|
||||||
typedef enum vieter_package_error {
|
typedef enum vieter_package_error {
|
||||||
vieter_package_ok = 0,
|
vieter_package_ok = 0,
|
||||||
vieter_package_unarchive_error = 1,
|
vieter_package_unarchive_error = 1,
|
||||||
vieter_package_stat_error = 2
|
vieter_package_stat_error = 2
|
||||||
} vieter_package_error;
|
} vieter_package_error;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -17,12 +17,13 @@ vieter_package *vieter_package_init();
|
||||||
/*
|
/*
|
||||||
* Parse package file into something usable by libvieter.
|
* Parse package file into something usable by libvieter.
|
||||||
*/
|
*/
|
||||||
vieter_package_error vieter_package_read_archive(vieter_package *pkg, const char *pkg_path);
|
vieter_package_error vieter_package_read_archive(vieter_package *pkg,
|
||||||
|
const char *pkg_path);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|||||||
* Deallocate a package.
|
* Deallocate a package.
|
||||||
*/
|
*/
|
||||||
void vieter_package_free(vieter_package ** ptp);
|
void vieter_package_free(vieter_package **ptp);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Create string that will become the package's desc file.
|
* Create string that will become the package's desc file.
|
||||||
|
|
|
||||||
|
|
@ -1,245 +1,246 @@
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <archive.h>
|
#include <archive.h>
|
||||||
#include <archive_entry.h>
|
#include <archive_entry.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include "vieter_package_internal.h"
|
|
||||||
#include "sha256.h"
|
#include "sha256.h"
|
||||||
|
#include "vieter_package_internal.h"
|
||||||
|
Jef Roosens
commented
General note for General note for `snprintf` usage: what happens if the value is larger than the buffer? Is it simply cut off? Cuz if so, we shouldn't be making assumptions about the length of the input.
GreekStapler
commented
I used I used `snprintf` because I didn't want to risk any buffer overflows, but it can truncate perfectly valid inputs if they are long enough (e.g. very long url). I'll turn `aux` into a malloc'd string and use the return value of `snprintf` to reallocate memory as is necessary without any overflows.
|
|||||||
|
|
||||||
#define ADD_STRING(section, field) if (pkg_info->field != 0) { \
|
#define ADD_STRING(section, field) \
|
||||||
size_to_be_written = snprintf(aux, small_buff_size, section, pkg_info->field); \
|
if (pkg_info->field != 0) { \
|
||||||
if (size_to_be_written > small_buff_size) { \
|
size_to_be_written = \
|
||||||
aux = realloc(aux, size_to_be_written + 1); \
|
snprintf(aux, small_buff_size, section, pkg_info->field); \
|
||||||
small_buff_size = size_to_be_written + 1; \
|
if (size_to_be_written > small_buff_size) { \
|
||||||
snprintf(aux, small_buff_size, section, pkg_info->field); \
|
aux = realloc(aux, size_to_be_written + 1); \
|
||||||
} \
|
small_buff_size = size_to_be_written + 1; \
|
||||||
if (buff_size < strlen(description) + small_buff_size + 1) { \
|
snprintf(aux, small_buff_size, section, pkg_info->field); \
|
||||||
description = realloc(description, buff_size * 2); \
|
} \
|
||||||
buff_size *= 2; \
|
if (buff_size < strlen(description) + small_buff_size + 1) { \
|
||||||
} \
|
description = realloc(description, buff_size * 2); \
|
||||||
strcat(description, aux); \
|
buff_size *= 2; \
|
||||||
}
|
} \
|
||||||
|
Jef Roosens
commented
This check is not safe, the dynarray code doesn't make any guarantees about empty fields being NULL, they're simply malloc'ed and not initialised. This check is not safe, the dynarray code doesn't make any guarantees about empty fields being NULL, they're simply malloc'ed and not initialised.
|
|||||||
|
strcat(description, aux); \
|
||||||
|
}
|
||||||
|
|
||||||
#define ADD_ARRAY(section, field) i = 0; if (pkg_info->field != NULL) { \
|
#define ADD_ARRAY(section, field) \
|
||||||
ADD_STRING(section, field->array[i]); i++; \
|
i = 0; \
|
||||||
while (pkg_info->field->array[i] != NULL) { \
|
if (pkg_info->field != NULL) { \
|
||||||
ADD_STRING("\n%s", field->array[i]); i++; \
|
ADD_STRING(section, field->array[i]); \
|
||||||
} \
|
i++; \
|
||||||
}
|
while (pkg_info->field->array[i] != NULL) { \
|
||||||
|
ADD_STRING("\n%s", field->array[i]); \
|
||||||
|
i++; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
static char *ignored_names[5] = {
|
static char *ignored_names[5] = {".BUILDINFO", ".INSTALL", ".MTREE", ".PKGINFO",
|
||||||
".BUILDINFO",
|
".CHANGELOG"};
|
||||||
".INSTALL",
|
|
||||||
".MTREE",
|
|
||||||
".PKGINFO",
|
|
||||||
".CHANGELOG"
|
|
||||||
};
|
|
||||||
static size_t ignored_words_len = sizeof(ignored_names) / sizeof(char *);
|
static size_t ignored_words_len = sizeof(ignored_names) / sizeof(char *);
|
||||||
|
|
||||||
vieter_package *vieter_package_init() {
|
vieter_package *vieter_package_init() {
|
||||||
return calloc(sizeof(vieter_package_info), 1);
|
return calloc(sizeof(vieter_package_info), 1);
|
||||||
|
GreekStapler marked this conversation as resolved
Outdated
Jef Roosens
commented
Shouldn't this be Shouldn't this be `sizeof(a package)`?
|
|||||||
}
|
}
|
||||||
|
|
||||||
vieter_package_error vieter_package_read_archive(vieter_package *pkg, const char *pkg_path) {
|
vieter_package_error vieter_package_read_archive(vieter_package *pkg,
|
||||||
struct archive *a = archive_read_new();
|
const char *pkg_path) {
|
||||||
struct archive_entry *entry = archive_entry_new();
|
struct archive *a = archive_read_new();
|
||||||
|
struct archive_entry *entry = archive_entry_new();
|
||||||
|
|
||||||
// These three are the most commonly used compression methods
|
// These three are the most commonly used compression methods
|
||||||
archive_read_support_filter_zstd(a);
|
archive_read_support_filter_zstd(a);
|
||||||
archive_read_support_filter_gzip(a);
|
archive_read_support_filter_gzip(a);
|
||||||
archive_read_support_filter_xz(a);
|
archive_read_support_filter_xz(a);
|
||||||
|
|
||||||
// Contents should always be a tarball
|
// Contents should always be a tarball
|
||||||
archive_read_support_format_tar(a);
|
archive_read_support_format_tar(a);
|
||||||
|
|
||||||
// TODO where does this 10240 come from?
|
// TODO where does this 10240 come from?
|
||||||
int r = archive_read_open_filename(a, pkg_path, 10240);
|
int r = archive_read_open_filename(a, pkg_path, 10240);
|
||||||
|
|
||||||
// Exit early if we weren't able to successfully open the archive for reading
|
// Exit early if we weren't able to successfully open the archive for reading
|
||||||
if (r != ARCHIVE_OK) {
|
if (r != ARCHIVE_OK) {
|
||||||
|
Jef Roosens
commented
Returning See the heap module for an example. Returning `NULL` when a function fails doesn't say anything about why the function failed. A better API imo (which I also use in the heap module) is for the function to return an enum value intead, indicating the success of the function. The output is written to a pointer-to-pointer that is passed as the first argument of the function.
See the heap module for an example.
|
|||||||
return vieter_package_unarchive_error;
|
return vieter_package_unarchive_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
int compression_code = archive_filter_code(a, 0);
|
int compression_code = archive_filter_code(a, 0);
|
||||||
const char *path_name;
|
const char *path_name;
|
||||||
|
|
||||||
vieter_package_info *pkg_info;
|
vieter_package_info *pkg_info;
|
||||||
vieter_package_dynarray *files = vieter_package_dynarray_init(16);
|
vieter_package_dynarray *files = vieter_package_dynarray_init(16);
|
||||||
vieter_package_dynarray_add(files, "%FILES%");
|
vieter_package_dynarray_add(files, "%FILES%");
|
||||||
|
|
||||||
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
while (archive_read_next_header(a, &entry) == ARCHIVE_OK) {
|
||||||
path_name = archive_entry_pathname(entry);
|
path_name = archive_entry_pathname(entry);
|
||||||
|
|
||||||
bool ignore = false;
|
bool ignore = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < ignored_words_len; i++) {
|
for (size_t i = 0; i < ignored_words_len; i++) {
|
||||||
if (strcmp(path_name, ignored_names[i]) == 0) {
|
if (strcmp(path_name, ignored_names[i]) == 0) {
|
||||||
ignore = true;
|
ignore = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ignore) {
|
if (!ignore) {
|
||||||
vieter_package_dynarray_add(files, path_name);
|
vieter_package_dynarray_add(files, path_name);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (strcmp(path_name, ".PKGINFO") == 0) {
|
if (strcmp(path_name, ".PKGINFO") == 0) {
|
||||||
// Read data of file into memory buffer
|
// Read data of file into memory buffer
|
||||||
int size = archive_entry_size(entry);
|
int size = archive_entry_size(entry);
|
||||||
char *buf = malloc(size);
|
char *buf = malloc(size);
|
||||||
archive_read_data(a, buf, size);
|
archive_read_data(a, buf, size);
|
||||||
|
|
||||||
// Parse package vieter_package_info string into a struct
|
// Parse package vieter_package_info string into a struct
|
||||||
pkg_info = vieter_package_info_init();
|
pkg_info = vieter_package_info_init();
|
||||||
vieter_package_info_parse(pkg_info, buf);
|
vieter_package_info_parse(pkg_info, buf);
|
||||||
|
|
||||||
free(buf);
|
free(buf);
|
||||||
} else {
|
} else {
|
||||||
archive_read_data_skip(a);
|
archive_read_data_skip(a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get size of file
|
// Get size of file
|
||||||
struct stat stats;
|
struct stat stats;
|
||||||
|
|
||||||
|
Jef Roosens
commented
Same here Same here
|
|||||||
if (stat(pkg_path, &stats) != 0) {
|
if (stat(pkg_path, &stats) != 0) {
|
||||||
// errno is set if stat() fails; the calling function should check
|
// errno is set if stat() fails; the calling function should check
|
||||||
// the value of errno in case vieter_package_stat_error is returned
|
// the value of errno in case vieter_package_stat_error is returned
|
||||||
return vieter_package_stat_error;
|
return vieter_package_stat_error;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkg_info->csize = stats.st_size;
|
pkg_info->csize = stats.st_size;
|
||||||
|
|
||||||
archive_read_free(a);
|
archive_read_free(a);
|
||||||
|
|
||||||
// Create final return value
|
// Create final return value
|
||||||
pkg->path = strdup(pkg_path);
|
pkg->path = strdup(pkg_path);
|
||||||
pkg->info = pkg_info;
|
pkg->info = pkg_info;
|
||||||
pkg->files = files;
|
pkg->files = files;
|
||||||
pkg->compression = compression_code;
|
pkg->compression = compression_code;
|
||||||
|
|
||||||
return vieter_package_ok;
|
return vieter_package_ok;
|
||||||
}
|
}
|
||||||
|
Jef Roosens
commented
As mentioned on matrix, this function should become a streaming one. As mentioned on matrix, this function should become a streaming one.
|
|||||||
|
|
||||||
void vieter_package_sha256sum(vieter_package *pkg, char *res) {
|
void vieter_package_sha256sum(vieter_package *pkg, char *res) {
|
||||||
FILE *f = fopen(pkg->path, "r");
|
FILE *f = fopen(pkg->path, "r");
|
||||||
// Try to read 100KiB at a time
|
// Try to read 100KiB at a time
|
||||||
unsigned char *in = malloc(102400);
|
unsigned char *in = malloc(102400);
|
||||||
// Actual number of bytes read
|
// Actual number of bytes read
|
||||||
size_t read_size;
|
size_t read_size;
|
||||||
|
|
||||||
SHA256_CTX *ctx = malloc(sizeof(SHA256_CTX));
|
SHA256_CTX *ctx = malloc(sizeof(SHA256_CTX));
|
||||||
sha256_init(ctx);
|
sha256_init(ctx);
|
||||||
while ((read_size = fread(in, 1, 102400, f)) != 0) {
|
while ((read_size = fread(in, 1, 102400, f)) != 0) {
|
||||||
sha256_update(ctx, in, read_size);
|
sha256_update(ctx, in, read_size);
|
||||||
}
|
}
|
||||||
unsigned char hash[SHA256_BLOCK_SIZE];
|
unsigned char hash[SHA256_BLOCK_SIZE];
|
||||||
|
|
||||||
sha256_final(ctx, hash);
|
sha256_final(ctx, hash);
|
||||||
|
|
||||||
fclose(f);
|
fclose(f);
|
||||||
free(in);
|
free(in);
|
||||||
free(ctx);
|
free(ctx);
|
||||||
|
|
||||||
// We need to convert the bytes in the hash to get a string representation of its hex values
|
// We need to convert the bytes in the hash to get a string representation of
|
||||||
// i.e. turn 1001 1111 into the string "9f"
|
// its hex values i.e. turn 1001 1111 into the string "9f" Each byte of the
|
||||||
// Each byte of the hash is going to turn into two bytes in the final string
|
// hash is going to turn into two bytes in the final string so we are going to
|
||||||
// so we are going to convert each half byte into a char
|
// convert each half byte into a char
|
||||||
unsigned int half_byte = 0;
|
unsigned int half_byte = 0;
|
||||||
int j = 0;
|
int j = 0;
|
||||||
|
|
||||||
// We advance 2 bytes in the string for every one byte of the hash
|
// We advance 2 bytes in the string for every one byte of the hash
|
||||||
for (int i = 0; i < SHA256_BLOCK_SIZE; i++) {
|
for (int i = 0; i < SHA256_BLOCK_SIZE; i++) {
|
||||||
// We transform the first half byte into the second character to keep
|
// We transform the first half byte into the second character to keep
|
||||||
|
Jef Roosens
commented
Oh smart, no array indexing required, didn't even think of that ;p Oh smart, no array indexing required, didn't even think of that ;p
|
|||||||
// each byte from becoming reversed in the final string
|
// each byte from becoming reversed in the final string
|
||||||
half_byte = hash[i] & 0b1111;
|
half_byte = hash[i] & 0b1111;
|
||||||
if (half_byte < 10) {
|
if (half_byte < 10) {
|
||||||
res[j+1] = half_byte + 48;
|
res[j + 1] = half_byte + 48;
|
||||||
} else {
|
} else {
|
||||||
res[j+1] = half_byte + 87;
|
res[j + 1] = half_byte + 87;
|
||||||
}
|
}
|
||||||
hash[i] = hash[i] >> 4;
|
hash[i] = hash[i] >> 4;
|
||||||
half_byte = hash[i] & 0b1111;
|
half_byte = hash[i] & 0b1111;
|
||||||
if (half_byte < 10) {
|
if (half_byte < 10) {
|
||||||
res[j] = half_byte + 48;
|
res[j] = half_byte + 48;
|
||||||
} else {
|
} else {
|
||||||
res[j] = half_byte + 87;
|
res[j] = half_byte + 87;
|
||||||
}
|
}
|
||||||
|
|
||||||
j += 2;
|
j += 2;
|
||||||
}
|
}
|
||||||
res[j] = '\0';
|
res[j] = '\0';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
char *vieter_package_to_description(vieter_package *pkg) {
|
char *vieter_package_to_description(vieter_package *pkg) {
|
||||||
|
Jef Roosens
commented
This should also use This should also use `SMALL_BUFF_SIZE` I think, just for consistency.
|
|||||||
vieter_package_info *pkg_info = pkg->info;
|
vieter_package_info *pkg_info = pkg->info;
|
||||||
|
|
||||||
size_t buff_size = 1024;
|
size_t buff_size = 1024;
|
||||||
int small_buff_size = 128;
|
int small_buff_size = 128;
|
||||||
int size_to_be_written;
|
int size_to_be_written;
|
||||||
char *aux = malloc(sizeof(char) * small_buff_size);
|
char *aux = malloc(sizeof(char) * small_buff_size);
|
||||||
char *description = malloc(sizeof(char) * buff_size);
|
char *description = malloc(sizeof(char) * buff_size);
|
||||||
// Helper variable for ADD_ARRAY macro
|
// Helper variable for ADD_ARRAY macro
|
||||||
|
Jef Roosens
commented
Vieter doesn't only supports zstd-compressed archives. Currently it also accepts xz- or gzip-compressed archives, so this should be accounted for. This choice was made noteably because Archlinux ARM uses xz-compressed archives instead of zstd. Vieter doesn't only supports zstd-compressed archives. Currently it also accepts xz- or gzip-compressed archives, so this should be accounted for.
This choice was made noteably because Archlinux ARM uses xz-compressed archives instead of zstd.
|
|||||||
int i;
|
int i;
|
||||||
|
|
||||||
// special case for FILENAME
|
// special case for FILENAME
|
||||||
// assuming .pkg.tar.zst; other formats are valid, this should account for that
|
// assuming .pkg.tar.zst; other formats are valid, this should account for
|
||||||
size_to_be_written = snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst", pkg_info->name,
|
// that
|
||||||
pkg_info->version, pkg_info->arch);
|
size_to_be_written =
|
||||||
|
snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst",
|
||||||
|
pkg_info->name, pkg_info->version, pkg_info->arch);
|
||||||
|
|
||||||
// We neither want to let an arbritrarily long input to overflow the buffer
|
// We neither want to let an arbritrarily long input to overflow the buffer
|
||||||
// nor to truncate perfectly valid inputs
|
// nor to truncate perfectly valid inputs
|
||||||
if (size_to_be_written > small_buff_size) {
|
if (size_to_be_written > small_buff_size) {
|
||||||
aux = realloc(aux, size_to_be_written + 1);
|
aux = realloc(aux, size_to_be_written + 1);
|
||||||
small_buff_size = size_to_be_written + 1;
|
small_buff_size = size_to_be_written + 1;
|
||||||
snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst", pkg_info->name,
|
snprintf(aux, small_buff_size, "%%FILENAME%%\n%s-%s-%s.pkg.tar.zst",
|
||||||
pkg_info->version, pkg_info->arch);
|
pkg_info->name, pkg_info->version, pkg_info->arch);
|
||||||
}
|
}
|
||||||
strcpy(description, aux);
|
strcpy(description, aux);
|
||||||
|
|
||||||
ADD_STRING("\n\n%%NAME%%\n%s", name);
|
ADD_STRING("\n\n%%NAME%%\n%s", name);
|
||||||
ADD_STRING("\n\n%%BASE%%\n%s", base);
|
ADD_STRING("\n\n%%BASE%%\n%s", base);
|
||||||
ADD_STRING("\n\n%%VERSION%%\n%s", version);
|
ADD_STRING("\n\n%%VERSION%%\n%s", version);
|
||||||
ADD_STRING("\n\n%%DESC%%\n%s", description);
|
ADD_STRING("\n\n%%DESC%%\n%s", description);
|
||||||
ADD_ARRAY("\n\n%%GROUPS%%\n%s", groups);
|
ADD_ARRAY("\n\n%%GROUPS%%\n%s", groups);
|
||||||
ADD_STRING("\n\n%%CSIZE%%\n%ld", csize);
|
ADD_STRING("\n\n%%CSIZE%%\n%ld", csize);
|
||||||
ADD_STRING("\n\n%%ISIZE%%\n%ld", size);
|
ADD_STRING("\n\n%%ISIZE%%\n%ld", size);
|
||||||
|
|
||||||
char checksum[SHA256_BLOCK_SIZE * 2 + 1];
|
char checksum[SHA256_BLOCK_SIZE * 2 + 1];
|
||||||
vieter_package_sha256sum(pkg, checksum);
|
vieter_package_sha256sum(pkg, checksum);
|
||||||
|
|
||||||
snprintf(aux, small_buff_size, "\n\n%%SHA256SUM%%\n%s", checksum);
|
snprintf(aux, small_buff_size, "\n\n%%SHA256SUM%%\n%s", checksum);
|
||||||
if (buff_size < strlen(description) + small_buff_size + 1) {
|
if (buff_size < strlen(description) + small_buff_size + 1) {
|
||||||
description = realloc(description, buff_size * 2);
|
description = realloc(description, buff_size * 2);
|
||||||
buff_size *= 2;
|
buff_size *= 2;
|
||||||
}
|
}
|
||||||
strcat(description, aux);
|
strcat(description, aux);
|
||||||
|
|
||||||
ADD_STRING("\n\n%%URL%%\n%s", url);
|
ADD_STRING("\n\n%%URL%%\n%s", url);
|
||||||
ADD_ARRAY("\n\n%%LICENSE%%\n%s", licenses);
|
ADD_ARRAY("\n\n%%LICENSE%%\n%s", licenses);
|
||||||
ADD_STRING("\n\n%%ARCH%%\n%s", arch);
|
ADD_STRING("\n\n%%ARCH%%\n%s", arch);
|
||||||
ADD_STRING("\n\n%%BUILDDATE%%\n%ld", build_date);
|
ADD_STRING("\n\n%%BUILDDATE%%\n%ld", build_date);
|
||||||
ADD_STRING("\n\n%%PACKAGER%%\n%s", packager);
|
ADD_STRING("\n\n%%PACKAGER%%\n%s", packager);
|
||||||
ADD_ARRAY("\n\n%%REPLACES%%\n%s", replaces);
|
ADD_ARRAY("\n\n%%REPLACES%%\n%s", replaces);
|
||||||
ADD_ARRAY("\n\n%%CONFLICTS%%\n%s", conflicts);
|
ADD_ARRAY("\n\n%%CONFLICTS%%\n%s", conflicts);
|
||||||
ADD_ARRAY("\n\n%%PROVIDES%%\n%s", provides);
|
ADD_ARRAY("\n\n%%PROVIDES%%\n%s", provides);
|
||||||
ADD_ARRAY("\n\n%%DEPENDS%%\n%s", depends);
|
ADD_ARRAY("\n\n%%DEPENDS%%\n%s", depends);
|
||||||
ADD_ARRAY("\n\n%%OPTDEPENDS%%\n%s", optdepends);
|
ADD_ARRAY("\n\n%%OPTDEPENDS%%\n%s", optdepends);
|
||||||
ADD_ARRAY("\n\n%%MAKEDEPENDS%%\n%s", makedepends);
|
ADD_ARRAY("\n\n%%MAKEDEPENDS%%\n%s", makedepends);
|
||||||
ADD_ARRAY("\n\n%%CHECKDEPENDS%%\n%s", checkdepends);
|
ADD_ARRAY("\n\n%%CHECKDEPENDS%%\n%s", checkdepends);
|
||||||
|
|
||||||
strcat(description, "\n\n");
|
strcat(description, "\n\n");
|
||||||
|
|
||||||
return description;
|
return description;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vieter_package_free(vieter_package **ptp) {
|
void vieter_package_free(vieter_package **ptp) {
|
||||||
FREE_STRING((*ptp)->path);
|
FREE_STRING((*ptp)->path);
|
||||||
vieter_package_info_free((*ptp)->info);
|
vieter_package_info_free((*ptp)->info);
|
||||||
vieter_package_dynarray_free((*ptp)->files);
|
vieter_package_dynarray_free((*ptp)->files);
|
||||||
free(*ptp);
|
free(*ptp);
|
||||||
*ptp = NULL;
|
*ptp = NULL;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,56 +1,58 @@
|
||||||
#include "vieter_package_dynarray.h"
|
#include "vieter_package_dynarray.h"
|
||||||
|
|
||||||
vieter_package_dynarray *vieter_package_dynarray_init(size_t initial_capacity) {
|
vieter_package_dynarray *vieter_package_dynarray_init(size_t initial_capacity) {
|
||||||
vieter_package_dynarray *da = malloc(sizeof(vieter_package_dynarray));
|
vieter_package_dynarray *da = malloc(sizeof(vieter_package_dynarray));
|
||||||
da->size = 0;
|
da->size = 0;
|
||||||
da->capacity = initial_capacity;
|
da->capacity = initial_capacity;
|
||||||
|
|
||||||
return da;
|
return da;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vieter_package_dynarray_add(vieter_package_dynarray *da, const char *s) {
|
void vieter_package_dynarray_add(vieter_package_dynarray *da, const char *s) {
|
||||||
// An empty vieter_package_dynarray does not have an allocated internal array yet
|
// An empty vieter_package_dynarray does not have an allocated internal array
|
||||||
if (da->size == 0) {
|
// yet
|
||||||
da->array = malloc(sizeof(char*) * da->capacity);
|
if (da->size == 0) {
|
||||||
|
da->array = malloc(sizeof(char *) * da->capacity);
|
||||||
|
|
||||||
// Initialise all char*'s to 0 so array[i] == NULL can be used to see if field is empty
|
// Initialise all char*'s to 0 so array[i] == NULL can be used to see if
|
||||||
memset(da->array, 0, sizeof(char*) * da->capacity);
|
// field is empty
|
||||||
}
|
memset(da->array, 0, sizeof(char *) * da->capacity);
|
||||||
// Double array size if it's full
|
}
|
||||||
else if (da->size == da->capacity) {
|
// Double array size if it's full
|
||||||
// if the realloc fails, access to memory in da->array is lost
|
else if (da->size == da->capacity) {
|
||||||
da->array = realloc(da->array, sizeof(char*) * da->capacity * 2);
|
// if the realloc fails, access to memory in da->array is lost
|
||||||
da->capacity *= 2;
|
da->array = realloc(da->array, sizeof(char *) * da->capacity * 2);
|
||||||
|
da->capacity *= 2;
|
||||||
|
|
||||||
// Same as the previous memset, but only for newly allocated pointers
|
// Same as the previous memset, but only for newly allocated pointers
|
||||||
memset(da->array + da->size, 0, sizeof(char*) * da->capacity / 2);
|
memset(da->array + da->size, 0, sizeof(char *) * da->capacity / 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
da->array[da->size] = strdup(s);
|
da->array[da->size] = strdup(s);
|
||||||
da->size++;
|
da->size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
void vieter_package_dynarray_free(vieter_package_dynarray *da) {
|
void vieter_package_dynarray_free(vieter_package_dynarray *da) {
|
||||||
if (da == NULL) {
|
if (da == NULL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (da->array != NULL) {
|
if (da->array != NULL) {
|
||||||
for (size_t i = 0; i < da->size; i++) {
|
for (size_t i = 0; i < da->size; i++) {
|
||||||
free(da->array[i]);
|
free(da->array[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(da->array);
|
free(da->array);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(da);
|
free(da);
|
||||||
}
|
}
|
||||||
|
|
||||||
char **vieter_package_dynarray_convert(vieter_package_dynarray *da) {
|
char **vieter_package_dynarray_convert(vieter_package_dynarray *da) {
|
||||||
char **array = da->array;
|
char **array = da->array;
|
||||||
|
|
||||||
da->array = NULL;
|
da->array = NULL;
|
||||||
vieter_package_dynarray_free(da);
|
vieter_package_dynarray_free(da);
|
||||||
|
|
||||||
return array;
|
return array;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,78 +2,85 @@
|
||||||
|
|
||||||
#include "vieter_package_info.h"
|
#include "vieter_package_info.h"
|
||||||
|
|
||||||
#define PKG_INFO_STRING(key_ptr, field) if ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \
|
#define PKG_INFO_STRING(key_ptr, field) \
|
||||||
value_ptr += strlen(key_ptr);\
|
if ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \
|
||||||
|
Jef Roosens
commented
How does this work? How does this work? `strstr` doens't write any terminating NULL bytes for the substring or anything, so won't this `strlen` call return the entire remaining size of the pkginfo string?
GreekStapler
commented
The The pkginfo string in here is The `strlen` is called on `key_ptr` which is just a small self contained string (e.g. "\ngroup = ") that is null terminated.
The pkginfo string in here is `value_ptr`.
|
|||||||
tail_ptr = strchr(value_ptr, '\n');\
|
value_ptr += strlen(key_ptr); \
|
||||||
tail_ptr[0] = '\0'; \
|
tail_ptr = strchr(value_ptr, '\n'); \
|
||||||
pkg_info->field = strdup(value_ptr); \
|
tail_ptr[0] = '\0'; \
|
||||||
tail_ptr[0] = '\n'; \
|
pkg_info->field = strdup(value_ptr); \
|
||||||
} value_ptr = tail_ptr;
|
tail_ptr[0] = '\n'; \
|
||||||
|
} \
|
||||||
|
value_ptr = tail_ptr;
|
||||||
|
|
||||||
#define PKG_INFO_INT(key_ptr, field) value_ptr = strstr(value_ptr, key_ptr) + strlen(key_ptr);\
|
#define PKG_INFO_INT(key_ptr, field) \
|
||||||
tail_ptr = strchr(value_ptr, '\n');\
|
value_ptr = strstr(value_ptr, key_ptr) + strlen(key_ptr); \
|
||||||
tail_ptr[0] = '\0'; \
|
tail_ptr = strchr(value_ptr, '\n'); \
|
||||||
pkg_info->field = atoi(value_ptr); \
|
tail_ptr[0] = '\0'; \
|
||||||
tail_ptr[0] = '\n'; \
|
pkg_info->field = atoi(value_ptr); \
|
||||||
value_ptr = tail_ptr;
|
tail_ptr[0] = '\n'; \
|
||||||
|
value_ptr = tail_ptr;
|
||||||
|
|
||||||
#define PKG_INFO_ARRAY(key_ptr, field) while((value_ptr = strstr(value_ptr, key_ptr)) != NULL){ \
|
#define PKG_INFO_ARRAY(key_ptr, field) \
|
||||||
value_ptr = value_ptr + strlen(key_ptr);\
|
while ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \
|
||||||
tail_ptr = strchr(value_ptr, '\n'); \
|
value_ptr = value_ptr + strlen(key_ptr); \
|
||||||
tail_ptr[0] = '\0'; \
|
tail_ptr = strchr(value_ptr, '\n'); \
|
||||||
if(pkg_info->field == NULL) { pkg_info->field = vieter_package_dynarray_init(4); } \
|
tail_ptr[0] = '\0'; \
|
||||||
vieter_package_dynarray_add(pkg_info->field, value_ptr); \
|
if (pkg_info->field == NULL) { \
|
||||||
tail_ptr[0] = '\n'; \
|
pkg_info->field = vieter_package_dynarray_init(4); \
|
||||||
value_ptr = tail_ptr;\
|
} \
|
||||||
} value_ptr = tail_ptr;
|
vieter_package_dynarray_add(pkg_info->field, value_ptr); \
|
||||||
|
tail_ptr[0] = '\n'; \
|
||||||
|
value_ptr = tail_ptr; \
|
||||||
|
} \
|
||||||
|
value_ptr = tail_ptr;
|
||||||
|
|
||||||
vieter_package_info *vieter_package_info_init() {
|
vieter_package_info *vieter_package_info_init() {
|
||||||
return calloc(1, sizeof(vieter_package_info));
|
return calloc(1, sizeof(vieter_package_info));
|
||||||
}
|
}
|
||||||
|
|
||||||
void vieter_package_info_free(vieter_package_info *pkg_info) {
|
void vieter_package_info_free(vieter_package_info *pkg_info) {
|
||||||
FREE_STRING(pkg_info->name);
|
FREE_STRING(pkg_info->name);
|
||||||
FREE_STRING(pkg_info->base);
|
FREE_STRING(pkg_info->base);
|
||||||
FREE_STRING(pkg_info->version);
|
FREE_STRING(pkg_info->version);
|
||||||
FREE_STRING(pkg_info->description);
|
FREE_STRING(pkg_info->description);
|
||||||
FREE_STRING(pkg_info->url);
|
FREE_STRING(pkg_info->url);
|
||||||
FREE_STRING(pkg_info->arch);
|
FREE_STRING(pkg_info->arch);
|
||||||
FREE_STRING(pkg_info->packager);
|
FREE_STRING(pkg_info->packager);
|
||||||
FREE_STRING(pkg_info->pgpsig);
|
FREE_STRING(pkg_info->pgpsig);
|
||||||
|
|
||||||
vieter_package_dynarray_free(pkg_info->groups);
|
vieter_package_dynarray_free(pkg_info->groups);
|
||||||
vieter_package_dynarray_free(pkg_info->licenses);
|
vieter_package_dynarray_free(pkg_info->licenses);
|
||||||
vieter_package_dynarray_free(pkg_info->replaces);
|
vieter_package_dynarray_free(pkg_info->replaces);
|
||||||
vieter_package_dynarray_free(pkg_info->depends);
|
vieter_package_dynarray_free(pkg_info->depends);
|
||||||
vieter_package_dynarray_free(pkg_info->conflicts);
|
vieter_package_dynarray_free(pkg_info->conflicts);
|
||||||
vieter_package_dynarray_free(pkg_info->provides);
|
vieter_package_dynarray_free(pkg_info->provides);
|
||||||
vieter_package_dynarray_free(pkg_info->optdepends);
|
vieter_package_dynarray_free(pkg_info->optdepends);
|
||||||
vieter_package_dynarray_free(pkg_info->makedepends);
|
vieter_package_dynarray_free(pkg_info->makedepends);
|
||||||
vieter_package_dynarray_free(pkg_info->checkdepends);
|
vieter_package_dynarray_free(pkg_info->checkdepends);
|
||||||
|
|
||||||
free(pkg_info);
|
free(pkg_info);
|
||||||
}
|
}
|
||||||
|
|
||||||
void vieter_package_info_parse(vieter_package_info *pkg_info, char *pkg_info_str) {
|
void vieter_package_info_parse(vieter_package_info *pkg_info,
|
||||||
char *value_ptr = pkg_info_str, *tail_ptr;
|
char *pkg_info_str) {
|
||||||
|
char *value_ptr = pkg_info_str, *tail_ptr;
|
||||||
PKG_INFO_STRING("\npkgname = ", name);
|
|
||||||
PKG_INFO_STRING("\npkgbase = ", base);
|
|
||||||
PKG_INFO_STRING("\npkgver = ", version);
|
|
||||||
PKG_INFO_STRING("\npkgdesc = ", description);
|
|
||||||
PKG_INFO_STRING("\nurl = ", url);
|
|
||||||
PKG_INFO_INT("\nbuilddate = ", build_date);
|
|
||||||
PKG_INFO_STRING("\npackager = ", packager);
|
|
||||||
PKG_INFO_INT("\nsize = ", size);
|
|
||||||
PKG_INFO_STRING("\narch = ", arch);
|
|
||||||
PKG_INFO_ARRAY("\nlicense = ", licenses);
|
|
||||||
PKG_INFO_ARRAY("\nreplaces = ", replaces);
|
|
||||||
PKG_INFO_ARRAY("\ngroup = ", groups);
|
|
||||||
PKG_INFO_ARRAY("\nconflict = ", conflicts);
|
|
||||||
PKG_INFO_ARRAY("\nprovides = ", provides);
|
|
||||||
PKG_INFO_ARRAY("\ndepend = ", depends);
|
|
||||||
PKG_INFO_ARRAY("\noptdepend = ", optdepends);
|
|
||||||
PKG_INFO_ARRAY("\nmakedepend = ", makedepends);
|
|
||||||
PKG_INFO_ARRAY("\ncheckdepend = ", checkdepends);
|
|
||||||
|
|
||||||
|
PKG_INFO_STRING("\npkgname = ", name);
|
||||||
|
PKG_INFO_STRING("\npkgbase = ", base);
|
||||||
|
PKG_INFO_STRING("\npkgver = ", version);
|
||||||
|
PKG_INFO_STRING("\npkgdesc = ", description);
|
||||||
|
PKG_INFO_STRING("\nurl = ", url);
|
||||||
|
PKG_INFO_INT("\nbuilddate = ", build_date);
|
||||||
|
PKG_INFO_STRING("\npackager = ", packager);
|
||||||
|
PKG_INFO_INT("\nsize = ", size);
|
||||||
|
PKG_INFO_STRING("\narch = ", arch);
|
||||||
|
PKG_INFO_ARRAY("\nlicense = ", licenses);
|
||||||
|
PKG_INFO_ARRAY("\nreplaces = ", replaces);
|
||||||
|
PKG_INFO_ARRAY("\ngroup = ", groups);
|
||||||
|
PKG_INFO_ARRAY("\nconflict = ", conflicts);
|
||||||
|
PKG_INFO_ARRAY("\nprovides = ", provides);
|
||||||
|
PKG_INFO_ARRAY("\ndepend = ", depends);
|
||||||
|
PKG_INFO_ARRAY("\noptdepend = ", optdepends);
|
||||||
|
PKG_INFO_ARRAY("\nmakedepend = ", makedepends);
|
||||||
|
PKG_INFO_ARRAY("\ncheckdepend = ", checkdepends);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
I don't personally use this style of free function right now, but it might be interesting to migrate all free functions to this style indeed. I'm assuming the pointer-to-pointer is so you can set the variable containing the pointer to NULL as well?
Yeah, dangling pointers can cause some real headaches, so I prefer to have free functions set the pointer to NULL themselves to make it less error prone.