/* * parser.c - MAKEPKG and mkpkg.conf parser * https://rawnix.org * * Copyright (c) 2025 zorz@gmx.com * SPDX-License-Identifier: BSD-2-Clause */ #include "mkpkg.h" #include #include #include #include #include /* cached once at first parse — avoids getenv() on every line */ static int debug_mode = -1; static int is_debug(void) { if (debug_mode < 0) debug_mode = (getenv("MKPKG_DEBUG") != NULL) ? 1 : 0; return debug_mode; } #define DBG(fmt, ...) \ do { if (is_debug()) fprintf(stderr, "[debug] " fmt "\n", ##__VA_ARGS__); } while (0) static void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fputs("mkpkg: error: ", stderr); vfprintf(stderr, fmt, ap); fputc('\n', stderr); va_end(ap); } /* ── Variable expansion ──────────────────────────────────────────────── */ /* * Expand $name, ${name}, and {name[N]} in str. * Known variables: name, version, release, and _custom vars. * Unknown variables are kept as-is. * Returns a newly allocated string — caller must free(). */ char *expand_vars(const char *str, const pkgfile_t *pkgfile) { char *result; size_t cap = LINE_MAX_LEN * 2; size_t len = 0; const char *p = str; if (!str) return NULL; result = xmalloc(cap); #define APPEND(ch) \ do { if (len + 1 < cap) result[len++] = (ch); } while (0) #define APPENDS(s) \ do { \ size_t _l = strlen(s); \ if (len + _l < cap) { memcpy(result + len, s, _l); len += _l; } \ } while (0) while (*p) { /* \$ — literal dollar */ if (*p == '\\' && *(p + 1) == '$') { APPEND('$'); p += 2; continue; } /* {varname[N]} — PyPI-style bare brace index */ if (*p == '{' && *(p + 1) != '\0') { const char *lk = p + 1; char varname[256]; size_t vi = 0; while (*lk && (isalnum((unsigned char)*lk) || *lk == '_') && vi < sizeof(varname) - 1) varname[vi++] = *lk++; varname[vi] = '\0'; if (vi > 0 && *lk == '[') { char *end; long idx = strtol(lk + 1, &end, 10); if (end != lk + 1 && *end == ']' && *(end + 1) == '}') { const char *val = NULL; int ci; if (strcmp(varname, "name") == 0) val = pkgfile->name; else if (strcmp(varname, "version") == 0) val = pkgfile->version; else if (strcmp(varname, "release") == 0) val = pkgfile->release; else { for (ci = 0; ci < pkgfile->custom_var_count; ci++) { if (strcmp(varname, pkgfile->custom_vars[ci].name) == 0) { val = pkgfile->custom_vars[ci].value; break; } } } if (val && idx >= 0 && (size_t)idx < strlen(val)) APPEND(val[idx]); p = end + 2; continue; } } /* not a recognised pattern — literal { */ APPEND(*p++); continue; } /* $name or ${name} */ if (*p == '$') { p++; int braced = (*p == '{'); if (braced) p++; char varname[256]; size_t vi = 0; while (*p && vi < sizeof(varname) - 1) { if (braced && *p == '}') { p++; break; } if (!braced && !isalnum((unsigned char)*p) && *p != '_') break; varname[vi++] = *p++; } varname[vi] = '\0'; const char *val = NULL; int ci; if (strcmp(varname, "name") == 0) val = pkgfile->name; else if (strcmp(varname, "version") == 0) val = pkgfile->version; else if (strcmp(varname, "release") == 0) val = pkgfile->release; else { for (ci = 0; ci < pkgfile->custom_var_count; ci++) { if (strcmp(varname, pkgfile->custom_vars[ci].name) == 0) { val = pkgfile->custom_vars[ci].value; break; } } } if (val) { APPENDS(val); } else { /* keep original */ APPEND('$'); if (braced) APPEND('{'); APPENDS(varname); if (braced) APPEND('}'); } continue; } APPEND(*p++); } result[len] = '\0'; return result; #undef APPEND #undef APPENDS } /* ── Token helpers ───────────────────────────────────────────────────── */ /* strip surrounding quotes from a token, in-place */ static char *strip_quotes(char *s) { size_t len; if (!s || !*s) return s; if (s[0] == '"' || s[0] == '\'') { char q = s[0]; len = strlen(s); if (len > 1 && s[len - 1] == q) { s[len - 1] = '\0'; return s + 1; } } return s; } /* strip inline comment: everything from # that is not inside quotes */ static void strip_comment(char *s) { int in_quote = 0; char qchar = 0; char *p; for (p = s; *p; p++) { if (!in_quote && (*p == '"' || *p == '\'')) { in_quote = 1; qchar = *p; } else if (in_quote && *p == qchar) { in_quote = 0; } else if (!in_quote && *p == '#') { *p = '\0'; break; } } } /* ── Array parser ────────────────────────────────────────────────────── */ /* * Parse shell array tokens into pkgfile fields. * Handles source=(), sha256sums=(), depends=(), etc. * `value` is the content between ( and ), possibly spanning multiple lines * (the caller has already assembled the full content). */ static void parse_array_tokens(const char *name, char *value, pkgfile_t *pkgfile) { char *saveptr = NULL; char *tok = strtok_r(value, " \t\n", &saveptr); int rename_idx = 0; int is_source = strcmp(name, "source") == 0; int is_sha256sums = strcmp(name, "sha256sums") == 0; int is_renames = strcmp(name, "renames") == 0; int is_groups = strcmp(name, "groups") == 0; int is_services = strcmp(name, "services") == 0; int is_permissions = strcmp(name, "permissions")== 0; int is_capabilities= strcmp(name, "capabilities")==0; int is_depends = strcmp(name, "depends") == 0; int is_makedepends = strcmp(name, "makedeps") == 0; int is_lockbox = strcmp(name, "lockbox") == 0; while (tok) { char *t = strip_quotes(tok); if (!t || !*t || strcmp(t, "''") == 0 || strcmp(t, "\"\"") == 0) { if (is_renames) rename_idx++; tok = strtok_r(NULL, " \t\n", &saveptr); continue; } if (is_source && pkgfile->source_count < MAX_SOURCES) { char *expanded = expand_vars(t, pkgfile); pkgfile->sources[pkgfile->source_count].url = expanded ? expanded : xstrdup(t); pkgfile->sources[pkgfile->source_count].rename = NULL; DBG("source[%d]: %s", pkgfile->source_count, pkgfile->sources[pkgfile->source_count].url); pkgfile->source_count++; } else if (is_sha256sums && pkgfile->sha256sum_count < MAX_SOURCES) { pkgfile->sha256sums[pkgfile->sha256sum_count++] = xstrdup(t); } else if (is_renames && rename_idx < pkgfile->source_count) { char *expanded = expand_vars(t, pkgfile); pkgfile->sources[rename_idx].rename = expanded ? expanded : xstrdup(t); if (strcmp(t, "SKIP") == 0) pkgfile->sources[rename_idx].rename = xstrdup("SKIP"); DBG("rename[%d]: %s", rename_idx, pkgfile->sources[rename_idx].rename); rename_idx++; } else if (is_groups && pkgfile->group_count < MAX_META_ITEMS) { pkgfile->groups[pkgfile->group_count++] = xstrdup(t); } else if (is_services && pkgfile->service_count < MAX_META_ITEMS) { pkgfile->services[pkgfile->service_count++] = xstrdup(t); } else if (is_permissions && pkgfile->permission_count < MAX_META_ITEMS) { pkgfile->permissions[pkgfile->permission_count++] = xstrdup(t); } else if (is_capabilities && pkgfile->capability_count < MAX_META_ITEMS) { pkgfile->capabilities[pkgfile->capability_count++] = xstrdup(t); } else if (is_depends && pkgfile->depend_count < MAX_DEPS) { pkgfile->depends[pkgfile->depend_count++] = xstrdup(t); } else if (is_makedepends && pkgfile->makedepend_count < MAX_DEPS) { pkgfile->makedepends[pkgfile->makedepend_count++] = xstrdup(t); } else if (is_lockbox) { if (strcmp(t, "yes") == 0 || strcmp(t, "on") == 0 || strcmp(t, "1") == 0) { pkgfile->has_lockbox = 1; } else if (strcmp(t, "no") == 0 || strcmp(t, "off") == 0) { /* explicit off */ } else if (pkgfile->lockbox_cmd_count < MAX_LOCKBOX_CMDS) { int idx = pkgfile->lockbox_cmd_count++; pkgfile->lockbox_cmds[idx] = xstrdup(t); pkgfile->lockbox_cmd_profiles[idx] = (strcmp(t, "cargo") == 0) ? LOCKBOX_PROFILE_CARGO : LOCKBOX_PROFILE_STANDARD; } } tok = strtok_r(NULL, " \t\n", &saveptr); } } /* * Parse an array assignment line, reading continuation lines from fp * if the closing ) is not on the same line. * Returns 1 if the line was an array we handle, 0 otherwise. */ static int parse_array(char *line, pkgfile_t *pkgfile, FILE *fp) { char *eq, *name, *value; char *full = NULL; size_t full_len, full_cap; char continuation[LINE_MAX_LEN]; eq = strchr(line, '='); if (!eq) return 0; /* make a working copy */ char linecopy[LINE_MAX_LEN]; snprintf(linecopy, sizeof(linecopy), "%s", line); eq = strchr(linecopy, '='); if (!eq) return 0; /* '=' lost to truncation — not an array line */ *eq = '\0'; name = str_trim(linecopy); value = str_trim(eq + 1); /* check if this is an array we handle */ if (strcmp(name, "source") != 0 && strcmp(name, "sha256sums") != 0 && strcmp(name, "renames") != 0 && strcmp(name, "groups") != 0 && strcmp(name, "services") != 0 && strcmp(name, "permissions") != 0 && strcmp(name, "capabilities") != 0 && strcmp(name, "depends") != 0 && strcmp(name, "makedeps") != 0 && strcmp(name, "lockbox") != 0) return 0; /* assemble full array content * * strip_comment is quote-aware so a `#` inside "..." or '...' isn't * treated as a comment. Without this, `sha256sums=( "abc..." # foo )` * would tokenise the comment text into fake checksum entries. */ strip_comment(value); full_cap = LINE_MAX_LEN * 4; full = xmalloc(full_cap); snprintf(full, full_cap, "%s", value); full_len = strlen(full); /* read continuation lines if ) not yet seen */ if (strchr(full, '(') && !strchr(full, ')')) { while (fgets(continuation, sizeof(continuation), fp)) { char *nl = strchr(continuation, '\n'); if (nl) *nl = '\0'; char *trimmed = str_trim(continuation); if (trimmed[0] == '#' || trimmed[0] == '\0') continue; /* strip inline comment from the continuation line too */ strip_comment(trimmed); str_trim(trimmed); if (!*trimmed) continue; /* grow buffer if needed */ size_t add = strlen(trimmed) + 2; if (full_len + add >= full_cap) { full_cap *= 2; full = xrealloc(full, full_cap); } full[full_len++] = ' '; memcpy(full + full_len, trimmed, strlen(trimmed)); full_len += strlen(trimmed); full[full_len] = '\0'; if (strchr(trimmed, ')')) break; } } /* strip parentheses */ char *content = full; if (*content == '(') content++; { size_t l = strlen(content); if (l > 0 && content[l - 1] == ')') content[l - 1] = '\0'; } DBG("array %s: %s", name, content); parse_array_tokens(name, content, pkgfile); free(full); return 1; } /* handle array+=(value) and array[n]=value */ static int parse_array_append(char *line, pkgfile_t *pkgfile) { char linecopy[LINE_MAX_LEN]; snprintf(linecopy, sizeof(linecopy), "%s", line); char *eq = strstr(linecopy, "+="); if (eq) { *eq = '\0'; char *name = str_trim(linecopy); char *value = str_trim(eq + 2); if (*value == '(') value++; value = str_trim(value); { size_t l = strlen(value); if (l > 0 && value[l - 1] == ')') value[l - 1] = '\0'; } value = strip_quotes(value); if (strcmp(name, "source") == 0 && pkgfile->source_count < MAX_SOURCES) { char *expanded = expand_vars(value, pkgfile); pkgfile->sources[pkgfile->source_count].url = expanded ? expanded : xstrdup(value); pkgfile->sources[pkgfile->source_count].rename = NULL; pkgfile->source_count++; return 1; } if (strcmp(name, "sha256sums") == 0 && pkgfile->sha256sum_count < MAX_SOURCES) { pkgfile->sha256sums[pkgfile->sha256sum_count++] = xstrdup(value); return 1; } return 0; } /* array[n]=value */ eq = strchr(linecopy, '='); if (!eq) return 0; char *bracket = strchr(linecopy, '['); if (!bracket || bracket >= eq) return 0; *bracket = '\0'; char *name = str_trim(linecopy); *eq = '\0'; char *value = str_trim(eq + 1); if (*value == '(') value++; value = strip_quotes(str_trim(value)); { size_t l = strlen(value); if (l > 0 && value[l - 1] == ')') value[l - 1] = '\0'; } if (strcmp(name, "source") == 0 && pkgfile->source_count < MAX_SOURCES) { char *expanded = expand_vars(value, pkgfile); pkgfile->sources[pkgfile->source_count].url = expanded ? expanded : xstrdup(value); pkgfile->sources[pkgfile->source_count].rename = NULL; pkgfile->source_count++; return 1; } if (strcmp(name, "sha256sums") == 0 && pkgfile->sha256sum_count < MAX_SOURCES) { pkgfile->sha256sums[pkgfile->sha256sum_count++] = xstrdup(value); return 1; } if (strcmp(name, "renames") == 0 && pkgfile->source_count > 0) { int idx = pkgfile->source_count - 1; pkgfile->sources[idx].rename = xstrdup(value); return 1; } return 0; } /* ── Variable assignment parser ──────────────────────────────────────── */ static void parse_variable(char *line, pkgfile_t *pkgfile) { char *eq = strchr(line, '='); if (!eq) return; char linecopy[LINE_MAX_LEN]; snprintf(linecopy, sizeof(linecopy), "%s", line); eq = strchr(linecopy, '='); if (!eq) return; /* '=' lost to truncation */ *eq = '\0'; char *name = str_trim(linecopy); char *value = str_trim(eq + 1); strip_comment(value); str_trim(value); value = strip_quotes(value); DBG("variable %s = %s", name, value); if (strcmp(name, "name") == 0) { free(pkgfile->name); pkgfile->name = xstrdup(value); } else if (strcmp(name, "version") == 0) { free(pkgfile->version); pkgfile->version = xstrdup(value); } else if (strcmp(name, "release") == 0) { free(pkgfile->release); pkgfile->release = xstrdup(value); } else if (name[0] == '_' && pkgfile->custom_var_count < MAX_CUSTOM_VARS) { int i = pkgfile->custom_var_count++; pkgfile->custom_vars[i].name = xstrdup(name); pkgfile->custom_vars[i].value = xstrdup(value); DBG("custom var %s = %s", name, value); } } /* ── Main parser ─────────────────────────────────────────────────────── */ int parse_pkgfile(const char *filename, pkgfile_t *pkgfile) { FILE *fp; char line[LINE_MAX_LEN]; char *magic; fp = fopen(filename, "r"); if (!fp) { error("cannot open MAKEPKG: %s", filename); return -1; } /* validate shebang */ if (!fgets(line, sizeof(line), fp)) { error("cannot read MAKEPKG: %s", filename); fclose(fp); return -1; } magic = str_trim(line); /* strip trailing newline before compare */ { size_t l = strlen(magic); while (l > 0 && (magic[l-1] == '\n' || magic[l-1] == '\r')) magic[--l] = '\0'; } if (strcmp(magic, "#!/bin/mkpkg") != 0) { error("not a mkpkg build file: %s", filename); fclose(fp); return -1; } memset(pkgfile, 0, sizeof(pkgfile_t)); /* current_function states */ enum { NONE, PRE_SHELL, EXTRACT, PATCH, BUILD, POST_BUILD, POST_SHELL, SIGNIFY } cur = NONE; int brace_count = 0; int seen_build = 0; while (fgets(line, sizeof(line), fp)) { /* strip newline */ { size_t l = strlen(line); while (l > 0 && (line[l-1] == '\n' || line[l-1] == '\r')) line[--l] = '\0'; } char *trimmed = str_trim(line); if (trimmed[0] == '\0' || trimmed[0] == '#') continue; DBG("line: %s", trimmed); /* function header detection */ if (cur == NONE) { if (str_starts_with(trimmed, "shell()")) { cur = seen_build ? POST_SHELL : PRE_SHELL; if (cur == PRE_SHELL) pkgfile->has_pre_shell = 1; else pkgfile->has_post_shell = 1; brace_count = 0; DBG("found shell() [%s]", cur == PRE_SHELL ? "pre" : "post"); continue; } if (str_starts_with(trimmed, "extract()")) { cur = EXTRACT; pkgfile->has_extract = 1; brace_count = 0; DBG("found extract()"); continue; } if (str_starts_with(trimmed, "patch()")) { cur = PATCH; pkgfile->has_patch = 1; brace_count = 0; DBG("found patch()"); continue; } if (str_starts_with(trimmed, "build()")) { cur = BUILD; seen_build = 1; brace_count = 0; DBG("found build()"); continue; } if (str_starts_with(trimmed, "post_build()")) { cur = POST_BUILD; pkgfile->has_post_build = 1; brace_count = 0; DBG("found post_build()"); continue; } if (str_starts_with(trimmed, "signify()")) { cur = SIGNIFY; pkgfile->has_signify = 1; brace_count = 0; DBG("found signify()"); continue; } /* variable / array */ if (!parse_array_append(trimmed, pkgfile)) if (!parse_array(trimmed, pkgfile, fp)) parse_variable(trimmed, pkgfile); continue; } /* inside a function body — brace counting must respect quotes, * otherwise `echo "}"` or sed expressions like `s/${VAR}/x/` * would mis-count and terminate the function early. */ { const char *p; int in_quote = 0; char qchar = 0; for (p = trimmed; *p; p++) { if (in_quote) { /* inside quotes: handle escape, look for close */ if (qchar == '"' && *p == '\\' && *(p + 1)) { p++; /* skip escaped char */ continue; } if (*p == qchar) in_quote = 0; continue; } if (*p == '"' || *p == '\'') { in_quote = 1; qchar = *p; continue; } if (*p == '#') /* rest of line is a comment */ break; if (*p == '{') brace_count++; if (*p == '}') brace_count--; } } /* skip the bare opening brace line */ if (strcmp(trimmed, "{") == 0) continue; /* end of function */ if (brace_count < 0 || strcmp(trimmed, "}") == 0) { cur = NONE; brace_count = 0; continue; } if (trimmed[0] == '\0') continue; /* collect signify key lines */ if (cur == SIGNIFY) { if (pkgfile->signify_pubkey == NULL) { pkgfile->signify_pubkey = xstrdup(trimmed); } else { size_t ol = strlen(pkgfile->signify_pubkey); size_t al = strlen(trimmed); char *combined = xmalloc(ol + 1 + al + 1); memcpy(combined, pkgfile->signify_pubkey, ol); combined[ol] = '\n'; memcpy(combined + ol + 1, trimmed, al); combined[ol + 1 + al] = '\0'; free(pkgfile->signify_pubkey); pkgfile->signify_pubkey = combined; } continue; } /* collect function body lines */ #define ADD_CMD(arr, cnt, max) \ do { if ((cnt) < (max)) (arr)[(cnt)++] = xstrdup(trimmed); } while (0) switch (cur) { case PRE_SHELL: ADD_CMD(pkgfile->pre_shell_cmds, pkgfile->pre_shell_count, MAX_BUILD_CMDS); break; case POST_SHELL: ADD_CMD(pkgfile->post_shell_cmds, pkgfile->post_shell_count, MAX_BUILD_CMDS); break; case EXTRACT: ADD_CMD(pkgfile->extract_cmds, pkgfile->extract_count, MAX_BUILD_CMDS); break; case PATCH: ADD_CMD(pkgfile->patch_cmds, pkgfile->patch_count, MAX_BUILD_CMDS); break; case BUILD: ADD_CMD(pkgfile->build_cmds, pkgfile->build_count, MAX_BUILD_CMDS); break; case POST_BUILD: ADD_CMD(pkgfile->post_build_cmds, pkgfile->post_build_count, MAX_BUILD_CMDS); break; default: break; } #undef ADD_CMD } fclose(fp); /* validate required fields */ if (!pkgfile->name) { error("variable 'name' not set in MAKEPKG"); return E_PKGFILE; } if (!pkgfile->version) { error("variable 'version' not set in MAKEPKG"); return E_PKGFILE; } if (!pkgfile->release) { error("variable 'release' not set in MAKEPKG"); return E_PKGFILE; } if (pkgfile->build_count == 0) { error("function 'build()' not defined in MAKEPKG"); return E_PKGFILE; } return 0; } /* ── Config parser ───────────────────────────────────────────────────── */ int parse_config(const char *filename, config_t *config) { FILE *fp; char line[LINE_MAX_LEN]; char *eq, *name, *value; fp = fopen(filename, "r"); if (!fp) return 0; /* config file is optional */ while (fgets(line, sizeof(line), fp)) { { size_t l = strlen(line); while (l > 0 && (line[l-1] == '\n' || line[l-1] == '\r')) line[--l] = '\0'; } char *trimmed = str_trim(line); if (trimmed[0] == '\0' || trimmed[0] == '#') continue; /* strip leading "export " */ if (str_starts_with(trimmed, "export ")) trimmed += 7; eq = strchr(trimmed, '='); if (!eq) continue; *eq = '\0'; name = str_trim(trimmed); value = str_trim(eq + 1); strip_comment(value); str_trim(value); value = strip_quotes(value); if (strcmp(name, "MKPKG_SOURCE_DIR") == 0) snprintf(config->source_dir, sizeof(config->source_dir), "%s", value); else if (strcmp(name, "MKPKG_PACKAGE_DIR") == 0) snprintf(config->package_dir, sizeof(config->package_dir), "%s", value); else if (strcmp(name, "MKPKG_WORK_DIR") == 0) snprintf(config->work_dir, sizeof(config->work_dir), "%s", value); else if (strcmp(name, "MKPKG_TMP_DIR") == 0) snprintf(config->tmp_dir, sizeof(config->tmp_dir), "%s", value); else if (strcmp(name, "MKPKG_SECRET_KEY") == 0) snprintf(config->secret_key, sizeof(config->secret_key), "%s", value); else if (strcmp(name, "MKPKG_IGNORE_FOOTPRINT") == 0) config->ignore_footprint |= (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0); else if (strcmp(name, "MKPKG_IGNORE_CHECKSUMS") == 0) config->ignore_checksums |= (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0); else if (strcmp(name, "MKPKG_IGNORE_SIGNATURE") == 0) config->ignore_signature |= (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0); else if (strcmp(name, "MKPKG_NO_STRIP") == 0) config->no_strip = (strcmp(value, "yes") == 0 || strcmp(value, "1") == 0); else if (strcmp(name, "MKPKG_COMPRESSION_MODE") == 0) snprintf(config->compression, sizeof(config->compression), "%s", value); /* CC, CXX, CFLAGS etc. — exported to the build shell via mkpkg.conf * directly; mkpkg itself does not need to parse them */ } fclose(fp); return 0; } /* ── free_pkgfile ────────────────────────────────────────────────────── */ void free_pkgfile(pkgfile_t *pkgfile) { int i; if (!pkgfile) return; free(pkgfile->name); free(pkgfile->version); free(pkgfile->release); for (i = 0; i < pkgfile->custom_var_count; i++) { free(pkgfile->custom_vars[i].name); free(pkgfile->custom_vars[i].value); } for (i = 0; i < pkgfile->source_count; i++) { free(pkgfile->sources[i].url); free(pkgfile->sources[i].rename); } for (i = 0; i < pkgfile->sha256sum_count; i++) free(pkgfile->sha256sums[i]); for (i = 0; i < pkgfile->pre_shell_count; i++) free(pkgfile->pre_shell_cmds[i]); for (i = 0; i < pkgfile->post_shell_count; i++) free(pkgfile->post_shell_cmds[i]); for (i = 0; i < pkgfile->extract_count; i++) free(pkgfile->extract_cmds[i]); for (i = 0; i < pkgfile->patch_count; i++) free(pkgfile->patch_cmds[i]); for (i = 0; i < pkgfile->build_count; i++) free(pkgfile->build_cmds[i]); for (i = 0; i < pkgfile->post_build_count; i++) free(pkgfile->post_build_cmds[i]); for (i = 0; i < pkgfile->group_count; i++) free(pkgfile->groups[i]); for (i = 0; i < pkgfile->service_count; i++) free(pkgfile->services[i]); for (i = 0; i < pkgfile->permission_count; i++) free(pkgfile->permissions[i]); for (i = 0; i < pkgfile->capability_count; i++) free(pkgfile->capabilities[i]); for (i = 0; i < pkgfile->depend_count; i++) free(pkgfile->depends[i]); for (i = 0; i < pkgfile->makedepend_count; i++) free(pkgfile->makedepends[i]); for (i = 0; i < pkgfile->lockbox_cmd_count; i++) free(pkgfile->lockbox_cmds[i]); free(pkgfile->signify_pubkey); memset(pkgfile, 0, sizeof(pkgfile_t)); }