summaryrefslogtreecommitdiffstats
path: root/src/opk.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/opk.c')
-rw-r--r--src/opk.c280
1 files changed, 280 insertions, 0 deletions
diff --git a/src/opk.c b/src/opk.c
new file mode 100644
index 0000000..14efe10
--- /dev/null
+++ b/src/opk.c
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2023 Patrick McDermott
+ *
+ * This file is part of opkg-opkg.
+ *
+ * opkg-opkg is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * opkg-opkg is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with opkg-opkg. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include "defs.h"
+#include "gzip.h"
+#include "opk.h"
+#include "ustar.h"
+
+struct opkg_opk_opk {
+ FILE *file;
+ char file_buffer[8192];
+ struct opkg_opk_gzip *outer_gzip;
+ struct opkg_opk_ustar *outer_ustar;
+ char *version_buffer;
+ size_t version_size;
+ struct opkg_opk_gzip *inner_gzip;
+ struct opkg_opk_ustar *inner_ustar;
+};
+
+static int
+_opkg_opk_opk_file_read(void *user_data, char **buffer, size_t *size)
+{
+ struct opkg_opk_opk *opk = user_data;
+
+ *buffer = opk->file_buffer;
+ *size = fread(opk->file_buffer, 1, sizeof(opk->file_buffer),
+ opk->file);
+ if (feof(opk->file)) {
+ return OPKG_OPK_END;
+ } else if (ferror(opk->file) || *size == 0) {
+ return OPKG_OPK_ERROR;
+ } else {
+ return OPKG_OPK_OK;
+ }
+}
+
+struct opkg_opk_opk *
+opkg_opk_opk_init_outer(const char *file_name)
+{
+ struct opkg_opk_opk *opk;
+
+ opk = malloc(sizeof(*opk));
+ if (opk == NULL) {
+ goto error0;
+ }
+
+ /* Open outer archive. */
+ opk->file = fopen(file_name, "rb");
+ if (opk->file == NULL) {
+ fprintf(stderr, "Error: Failed to open file \"%s\"\n",
+ file_name);
+ goto error0;
+ }
+
+ /* Initialize outer gzip decompressor. */
+ opk->outer_gzip = opkg_opk_gzip_init(&_opkg_opk_opk_file_read, opk);
+ if (opk->outer_gzip == NULL) {
+ fputs("Error: Failed to initialize\n", stderr);
+ goto error1;
+ }
+
+ /* Initialize outer ustar unarchiver. */
+ opk->outer_ustar = opkg_opk_ustar_init(opk->outer_gzip);
+ if (opk->outer_ustar == NULL) {
+ fputs("Error: Failed to initialize\n", stderr);
+ goto error2;
+ }
+
+ /* Check package version. */
+ if (opkg_opk_ustar_seek(opk->outer_ustar, 1, "debian-binary") !=
+ OPKG_OPK_OK) {
+ fputs("Error: Failed to find \"debian-binary\" in archive\n",
+ stderr);
+ goto error3;
+ }
+ if (opkg_opk_ustar_read(opk->outer_ustar,
+ &opk->version_buffer, &opk->version_size) !=
+ OPKG_OPK_OK) {
+ fputs("Error: Failed to read \"debian-binary\" in archive\n",
+ stderr);
+ goto error3;
+ }
+ if (opk->version_size < 4 || strncmp(opk->version_buffer, "2.", 2) != 0)
+ {
+ fputs("Error: Unsupported package version\n", stderr);
+ goto error3;
+ }
+
+ return opk;
+
+ error3:
+ opkg_opk_ustar_free(opk->outer_ustar);
+ error2:
+ opkg_opk_gzip_free(opk->outer_gzip);
+ error1:
+ fclose(opk->file);
+ error0:
+ return NULL;
+}
+
+int
+opkg_opk_opk_init_inner(struct opkg_opk_opk *opk, const char *member)
+{
+ int ret;
+
+ /* Finish reading previous inner archive, if any. */
+ while ((ret = opkg_opk_ustar_read(opk->outer_ustar, NULL, NULL)) ==
+ OPKG_OPK_OK);
+ if (ret == OPKG_OPK_ERROR) {
+ fputs("Error: Failed to read archive\n", stderr);
+ return OPKG_OPK_ERROR;
+ }
+
+ /* Find requested inner archive. */
+ if (opkg_opk_ustar_seek(opk->outer_ustar, 1, member) !=
+ OPKG_OPK_OK) {
+ fprintf(stderr, "Error: Failed to find \"%s\" in archive\n",
+ member);
+ return OPKG_OPK_ERROR;
+ }
+
+ /* Initialize inner gzip decompressor. */
+ opk->inner_gzip = opkg_opk_gzip_init(
+ (opkg_opk_gzip_read_func *) &opkg_opk_ustar_read,
+ opk->outer_ustar);
+ if (opk->inner_gzip == NULL) {
+ fputs("Error: Failed to initialize\n", stderr);
+ return OPKG_OPK_ERROR;
+ }
+
+ /* Initialize inner ustar unarchiver. */
+ opk->inner_ustar = opkg_opk_ustar_init(opk->inner_gzip);
+ if (opk->inner_ustar == NULL) {
+ fputs("Error: Failed to initialize\n", stderr);
+ opkg_opk_gzip_free(opk->inner_gzip);
+ return OPKG_OPK_ERROR;
+ }
+
+ return OPKG_OPK_OK;
+}
+
+int
+opkg_opk_opk_read_control(struct opkg_opk_opk *opk)
+{
+ char *buffer;
+ size_t size;
+ int ret;
+
+ if (opkg_opk_ustar_seek(opk->inner_ustar, 2, "control", "./control") !=
+ OPKG_OPK_OK) {
+ fputs("Error: Failed to find control file\n", stderr);
+ return OPKG_OPK_ERROR;
+ }
+ while ((ret = opkg_opk_ustar_read(opk->inner_ustar, &buffer, &size)) ==
+ OPKG_OPK_OK) {
+ fwrite(buffer, 1, size, stdout);
+ }
+ if (ret == OPKG_OPK_ERROR) {
+ fputs("Error: Failed to read control file\n", stderr);
+ return OPKG_OPK_ERROR;
+ }
+
+ return OPKG_OPK_OK;
+}
+
+int
+opkg_opk_opk_list_members(struct opkg_opk_opk *opk)
+{
+ struct opkg_opk_ustar_member *head;
+ struct opkg_opk_ustar_member *tail;
+ struct opkg_opk_ustar_member *member;
+ int ret;
+ size_t uname_len;
+ size_t uname_len_max;
+ size_t gname_len;
+ size_t gname_len_max;
+ uint64_t size_max;
+ char fmt[29];
+ /* "%c%s %-32s/%-32s %11d %s %s\n" */
+ char mode[10];
+ char mtime[20];
+
+ /* Build singly-linked list and find maximum column widths. */
+ head = NULL;
+ uname_len_max = 0;
+ gname_len_max = 0;
+ size_max = 0;
+ while ((ret = opkg_opk_ustar_list(opk->inner_ustar, &member)) ==
+ OPKG_OPK_OK) {
+ if (head == NULL) {
+ head = member;
+ } else {
+ tail->next = member;
+ }
+ tail = member;
+ uname_len = strlen(member->uname);
+ if (uname_len > uname_len_max) {
+ uname_len_max = uname_len;
+ }
+ gname_len = strlen(member->gname);
+ if (gname_len > gname_len_max) {
+ gname_len_max = gname_len;
+ }
+ if (member->size > size_max) {
+ size_max = member->size;
+ }
+ }
+ if (ret == OPKG_OPK_ERROR) {
+ fputs("Error: Failed to list data files\n", stderr);
+ return OPKG_OPK_ERROR;
+ }
+ tail->next = NULL;
+
+ /* Print and free members. */
+ snprintf(fmt, sizeof(fmt), "%%c%%s %%-%zus/%%-%zus %%%lid %%s %%s\n",
+ uname_len, gname_len, lrint(ceil(log10(size_max))));
+ for (member = head; member != NULL;) {
+ if (member->mode & 00400) mode[0] = 'r'; else mode[0] = '-';
+ if (member->mode & 00200) mode[1] = 'w'; else mode[1] = '-';
+ if (member->mode & 00100) mode[2] = 'x'; else mode[2] = '-';
+ if (member->mode & 04000) mode[2] = 's';
+ if (member->mode & 00040) mode[3] = 'r'; else mode[3] = '-';
+ if (member->mode & 00020) mode[4] = 'w'; else mode[4] = '-';
+ if (member->mode & 00010) mode[5] = 'x'; else mode[5] = '-';
+ if (member->mode & 02000) mode[5] = 's';
+ if (member->mode & 00004) mode[6] = 'r'; else mode[6] = '-';
+ if (member->mode & 00002) mode[7] = 'w'; else mode[7] = '-';
+ if (member->mode & 00001) mode[8] = 'x'; else mode[8] = '-';
+ if (member->mode & 01000) mode[8] = 't';
+ mode[9] = '\0';
+ strftime(mtime, sizeof(mtime), "%Y-%m-%d %H:%M:%S",
+ localtime(&member->mtime));
+ printf(fmt,
+ member->type, mode,
+ member->uname, member->gname,
+ member->size, mtime, member->name);
+ head = member;
+ member = member->next;
+ free(head);
+ }
+
+ return OPKG_OPK_OK;
+}
+
+void
+opkg_opk_opk_free_inner(struct opkg_opk_opk *opk)
+{
+ opkg_opk_ustar_free(opk->inner_ustar);
+ opkg_opk_gzip_free(opk->inner_gzip);
+}
+
+void
+opkg_opk_opk_free_outer(struct opkg_opk_opk *opk)
+{
+ opkg_opk_ustar_free(opk->outer_ustar);
+ opkg_opk_gzip_free(opk->outer_gzip);
+ fclose(opk->file);
+ free(opk);
+}