diff --git a/src/package/c/package_info.c b/src/package/c/package_info.c index a8959ff..79d8720 100644 --- a/src/package/c/package_info.c +++ b/src/package/c/package_info.c @@ -1,3 +1,5 @@ +#include + #include "package_info.h" PkgInfo *package_info_init() { @@ -41,6 +43,108 @@ void package_info_free(PkgInfo **ptp) { *ptp = NULL; } -void package_info_parse(PkgInfo *pkg_info, const char *pkg_info_str) { +/** + * Advance the pointer until all spaces are skipped. + */ +static inline char *trim_spaces_front(char *ptr) { + while (ptr[0] == ' ') { + ptr++; + } + 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) { 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; } diff --git a/src/package/c/package_info.h b/src/package/c/package_info.h index 7e16f4b..c3df790 100644 --- a/src/package/c/package_info.h +++ b/src/package/c/package_info.h @@ -33,7 +33,7 @@ typedef struct pkg_info { } PkgInfo; PkgInfo *package_info_init(); -void package_info_parse(PkgInfo *pkg_info, const char *pkg_info_str); +int package_info_parse(PkgInfo *pkg_info, char *pkg_info_str); void package_info_free(PkgInfo **ptp); #endif