refactor: Modified .PKGINFO parser not to rely on gotos and continues. Parser is now optimistic and assumes .PKGINFO file is valid.

After my changes to the macros, they still feel a bit hacky, but I'm content with them. I also changed the parser to assume the .PKGINFO files are always valid because they are automatically generated. The parser also assumes the same fields will always appear in the same fixed order. I made this change after checking how makepkg generated this file.
pull/6/head
GreekStapler 2023-01-25 16:22:22 +01:00
parent c2313fe0be
commit 4309ad8cc5
2 changed files with 47 additions and 107 deletions

View File

@ -33,7 +33,7 @@ typedef struct pkg_info {
} PkgInfo;
PkgInfo *package_info_init();
int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str);
void package_info_parse(PkgInfo *pkg_info, char *pkg_info_str);
void package_info_free(PkgInfo *pkg_info);
#endif

View File

@ -2,6 +2,31 @@
#include "package_info.h"
#define PKG_INFO_STRING(key_ptr, field) if ((value_ptr = strstr(value_ptr, key_ptr)) != NULL) { \
value_ptr += strlen(key_ptr);\
tail_ptr = strchr(value_ptr, '\n');\
tail_ptr[0] = '\0'; \
pkg_info->field = strdup(value_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);\
tail_ptr = strchr(value_ptr, '\n');\
tail_ptr[0] = '\0'; \
pkg_info->field = atoi(value_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){ \
value_ptr = value_ptr + strlen(key_ptr);\
tail_ptr = strchr(value_ptr, '\n'); \
tail_ptr[0] = '\0'; \
if(pkg_info->field == NULL) { pkg_info->field = dynarray_init(4); } \
dynarray_add(pkg_info->field, value_ptr); \
tail_ptr[0] = '\n'; \
value_ptr = tail_ptr;\
} value_ptr = tail_ptr;
PkgInfo *package_info_init() {
return calloc(1, sizeof(PkgInfo));
}
@ -29,111 +54,26 @@ void package_info_free(PkgInfo *pkg_info) {
free(pkg_info);
}
/**
* Advance the pointer until all spaces are skipped.
*/
static inline char *trim_spaces_front(char *ptr) {
while (ptr[0] == ' ') {
ptr++;
}
void package_info_parse(PkgInfo *pkg_info, 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);
return ptr;
}
/**
* Given a string pointer in the middle of a string, move over all spaces in the
* given direction. The final space is replaced with a NULL-byte.
*/
static inline void trim_spaces_back(char *ptr) {
if (ptr[0] != ' ') {
return;
}
while (ptr[-1] == ' ') {
ptr--;
}
ptr[0] = '\0';
}
#define PKG_INFO_STRING(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = strdup(value_ptr); goto advance; }
#define PKG_INFO_INT(key, field) if (strcmp(key_ptr, key) == 0) { pkg_info->field = atoi(value_ptr); goto advance; }
#define PKG_INFO_ARRAY(key, field) if (strcmp(key_ptr, key) == 0) { \
if (pkg_info->field == NULL) { pkg_info->field = dynarray_init(4); } \
dynarray_add(pkg_info->field, value_ptr); goto advance; \
}
int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str) {
char *offset_ptr, *equals_ptr, *key_ptr, *value_ptr;
bool end = false;
// Iterate over all lines in file
while (!end) {
// This pointer will always point to the final character of the
// current line, be it the position of a newline or the NULL byte at
// the end of the entire string
offset_ptr = strchr(pkg_info_str, '\n');
// We replace the newline with a NULL byte. Now we know the line runs
// until the next NULL byte.
if (offset_ptr != NULL) {
offset_ptr[0] = '\0';
} else {
// Advance pointer to the NULL byte of the string
offset_ptr = pkg_info_str + 1;
while (*offset_ptr != '\0') {
offset_ptr++;
}
end = true;
}
// Skip comment lines
if (pkg_info_str[0] == '#') {
goto advance;
}
equals_ptr = strchr(pkg_info_str, '=');
// If a line doesn't contain an equals sign, the file is invalid
if (equals_ptr == NULL) {
return 1;
}
// Trim whitespace from key
key_ptr = trim_spaces_front(pkg_info_str);
trim_spaces_back(equals_ptr - 1);
// Trim spaces from value
value_ptr = trim_spaces_front(equals_ptr + 1);
trim_spaces_back(offset_ptr - 1);
// Match key
PKG_INFO_STRING("pkgname", name);
PKG_INFO_STRING("pkgbase", base);
PKG_INFO_STRING("pkgver", version);
PKG_INFO_STRING("pkgdesc", description);
PKG_INFO_INT("size", size);
PKG_INFO_STRING("url", url);
PKG_INFO_STRING("arch", arch);
PKG_INFO_INT("builddate", build_date);
PKG_INFO_STRING("packager", packager);
PKG_INFO_STRING("pgpsig", pgpsig);
PKG_INFO_INT("pgpsigsize", pgpsigsize);
PKG_INFO_ARRAY("group", groups);
PKG_INFO_ARRAY("license", licenses);
PKG_INFO_ARRAY("replaces", replaces);
PKG_INFO_ARRAY("depend", depends);
PKG_INFO_ARRAY("optdepend", optdepends);
PKG_INFO_ARRAY("makedepend", makedepends);
PKG_INFO_ARRAY("checkdepend", checkdepends);
advance:
pkg_info_str = offset_ptr + 1;
continue;
}
return 0;
}