libopkg: flag package with SF_NEED_DETAIL in pkg_init_from_file()
[oweals/opkg-lede.git] / libopkg / pkg.c
index 278b7271d8893717cad42f48d8301296af9c9c47..a76e152d4420c49153eac0eaff2316cc055a3ad6 100644 (file)
    General Public License for more details.
 */
 
-#include "includes.h"
-#include <ctype.h>
-#include <alloca.h>
+#include "config.h"
+
+#include <stdio.h>
 #include <string.h>
-#include <stdbool.h>
-#include <errno.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <libgen.h>
 
 #include "pkg.h"
 
 #include "opkg_conf.h"
 
 typedef struct enum_map enum_map_t;
-struct enum_map
-{
-     int value;
-     char *str;
+struct enum_map {
+       unsigned int value;
+       const char *str;
 };
 
 static const enum_map_t pkg_state_want_map[] = {
-     SW_UNKNOWN, "unknown"},
-     SW_INSTALL, "install"},
-     SW_DEINSTALL, "deinstall"},
-     SW_PURGE, "purge"}
+       {SW_UNKNOWN, "unknown"},
+       {SW_INSTALL, "install"},
+       {SW_DEINSTALL, "deinstall"},
+       {SW_PURGE, "purge"}
 };
 
 static const enum_map_t pkg_state_flag_map[] = {
-     SF_OK, "ok"},
-     SF_REINSTREQ, "reinstreq"},
-     SF_HOLD, "hold"},
-     SF_REPLACE, "replace"},
-     SF_NOPRUNE, "noprune"},
-     SF_PREFER, "prefer"},
-     SF_OBSOLETE, "obsolete"},
-     SF_USER, "user"},
+       {SF_OK, "ok"},
+       {SF_REINSTREQ, "reinstreq"},
+       {SF_HOLD, "hold"},
+       {SF_REPLACE, "replace"},
+       {SF_NOPRUNE, "noprune"},
+       {SF_PREFER, "prefer"},
+       {SF_OBSOLETE, "obsolete"},
+       {SF_USER, "user"},
 };
 
 static const enum_map_t pkg_state_status_map[] = {
-     { SS_NOT_INSTALLED, "not-installed" },
-     { SS_UNPACKED, "unpacked" },
-     { SS_HALF_CONFIGURED, "half-configured" },
-     { SS_INSTALLED, "installed" },
-     { SS_HALF_INSTALLED, "half-installed" },
-     { SS_CONFIG_FILES, "config-files" },
-     { SS_POST_INST_FAILED, "post-inst-failed" },
-     { SS_REMOVAL_FAILED, "removal-failed" }
+       {SS_NOT_INSTALLED, "not-installed"},
+       {SS_UNPACKED, "unpacked"},
+       {SS_HALF_CONFIGURED, "half-configured"},
+       {SS_INSTALLED, "installed"},
+       {SS_HALF_INSTALLED, "half-installed"},
+       {SS_CONFIG_FILES, "config-files"},
+       {SS_POST_INST_FAILED, "post-inst-failed"},
+       {SS_REMOVAL_FAILED, "removal-failed"}
 };
 
-static void
-pkg_init(pkg_t *pkg)
+static void pkg_init(pkg_t * pkg)
 {
-     pkg->name = NULL;
-     pkg->epoch = 0;
-     pkg->version = NULL;
-     pkg->revision = NULL;
-     pkg->dest = NULL;
-     pkg->src = NULL;
-     pkg->architecture = NULL;
-     pkg->maintainer = NULL;
-     pkg->section = NULL;
-     pkg->description = NULL;
-     pkg->state_want = SW_UNKNOWN;
-     pkg->state_flag = SF_OK;
-     pkg->state_status = SS_NOT_INSTALLED;
-     pkg->depends_str = NULL;
-     pkg->provides_str = NULL;
-     pkg->depends_count = 0;
-     pkg->depends = NULL;
-     pkg->suggests_str = NULL;
-     pkg->recommends_str = NULL;
-     pkg->suggests_count = 0;
-     pkg->recommends_count = 0;
-     
-     active_list_init(&pkg->list);
-
-     /* Abhaya: added init for conflicts fields */
-     pkg->conflicts = NULL;
-     pkg->conflicts_count = 0;
-
-     /* added for replaces.  Jamey 7/23/2002 */
-     pkg->replaces = NULL;
-     pkg->replaces_count = 0;
-    
-     pkg->pre_depends_count = 0;
-     pkg->pre_depends_str = NULL;
-     pkg->provides_count = 0;
-     pkg->provides = NULL;
-     pkg->filename = NULL;
-     pkg->local_filename = NULL;
-     pkg->tmp_unpack_dir = NULL;
-     pkg->md5sum = NULL;
-#if defined HAVE_SHA256
-     pkg->sha256sum = NULL;
-#endif
-     pkg->size = NULL;
-     pkg->installed_size = NULL;
-     pkg->priority = NULL;
-     pkg->source = NULL;
-     conffile_list_init(&pkg->conffiles);
-     pkg->installed_files = NULL;
-     pkg->installed_files_ref_cnt = 0;
-     pkg->essential = 0;
-     pkg->provided_by_hand = 0;
+       pkg->name = NULL;
+       pkg->dest = NULL;
+       pkg->src = NULL;
+       pkg->state_want = SW_UNKNOWN;
+       pkg->state_flag = SF_OK;
+       pkg->state_status = SS_NOT_INSTALLED;
+
+       pkg->installed_files = NULL;
+       pkg->installed_files_ref_cnt = 0;
+       pkg->essential = 0;
+       pkg->provided_by_hand = 0;
+
+       pkg->arch_index = 0;
+
+       blob_buf_init(&pkg->blob, 0);
+}
+
+pkg_t *pkg_new(void)
+{
+       pkg_t *pkg;
+
+       pkg = xcalloc(1, sizeof(pkg_t));
+       pkg_init(pkg);
+
+       return pkg;
+}
+
+void *pkg_set_raw(pkg_t *pkg, int id, const void *val, size_t len)
+{
+       int rem;
+       struct blob_attr *cur;
+
+       blob_for_each_attr(cur, pkg->blob.head, rem) {
+               if (blob_id(cur) == id) {
+                       if (blob_len(cur) < len) {
+                               fprintf(stderr, "ERROR: truncating field %d <%p> to %d byte",
+                                       id, val, blob_len(cur));
+                       }
+                       memcpy(blob_data(cur), val, blob_len(cur));
+                       return blob_data(cur);
+               }
+       }
+
+       cur = blob_put(&pkg->blob, id, val, len);
+       return cur ? blob_data(cur) : NULL;
 }
 
-pkg_t *
-pkg_new(void)
+void *pkg_get_raw(const pkg_t * pkg, int id)
 {
-     pkg_t *pkg;
+       int rem;
+       struct blob_attr *cur;
 
-     pkg = xcalloc(1, sizeof(pkg_t));
-     pkg_init(pkg);
+       blob_for_each_attr(cur, pkg->blob.head, rem)
+               if (blob_id(cur) == id)
+                       return blob_data(cur);
 
-     return pkg;
+       return NULL;
 }
 
-static void
-compound_depend_deinit(compound_depend_t *depends)
+char *pkg_set_string(pkg_t *pkg, int id, const char *s)
+{
+       size_t len;
+       char *p;
+
+       if (!s)
+               return NULL;
+
+       len = strlen(s);
+
+       while (isspace(*s)) {
+               s++;
+               len--;
+       }
+
+       while (len > 0 && isspace(s[len - 1]))
+               len--;
+
+       if (!len)
+               return NULL;
+
+       p = pkg_set_raw(pkg, id, s, len + 1);
+       p[len] = 0;
+
+       return p;
+}
+
+char *pkg_get_architecture(const pkg_t *pkg)
+{
+       nv_pair_list_elt_t *l;
+       int n = 1;
+
+       list_for_each_entry(l, &conf->arch_list.head, node) {
+               nv_pair_t *nv = (nv_pair_t *) l->data;
+               if (n++ == pkg->arch_index)
+                       return nv->name;
+       }
+
+       return NULL;
+}
+
+char *pkg_set_architecture(pkg_t *pkg, const char *architecture, ssize_t len)
+{
+       nv_pair_list_elt_t *l;
+       int n = 1;
+
+       list_for_each_entry(l, &conf->arch_list.head, node) {
+               nv_pair_t *nv = (nv_pair_t *) l->data;
+
+               if (!strncmp(nv->name, architecture, len) && nv->name[len] == '\0') {
+                       if (n >= 8) {
+                               opkg_msg(ERROR, "Internal error: too many different architectures\n");
+                               break;
+                       }
+
+                       pkg->arch_index = n;
+                       return nv->name;
+               }
+
+               n++;
+       }
+
+       pkg->arch_index = 0;
+       return NULL;
+}
+
+int pkg_get_arch_priority(const pkg_t *pkg)
+{
+       nv_pair_list_elt_t *l;
+       int n = 1;
+
+       list_for_each_entry(l, &conf->arch_list.head, node) {
+               nv_pair_t *nv = (nv_pair_t *) l->data;
+               if (n++ == pkg->arch_index)
+                       return strtol(nv->value, NULL, 0);
+       }
+
+       return 0;
+}
+
+char *pkg_get_md5(const pkg_t *pkg)
+{
+       char *p = pkg_get_raw(pkg, PKG_MD5SUM);
+
+       if (!p)
+               return NULL;
+
+       return checksum_bin2hex(p, 16);
+}
+
+char *pkg_set_md5(pkg_t *pkg, const char *cksum)
+{
+       size_t len;
+       char *p = checksum_hex2bin(cksum, &len);
+
+       if (!p || len != 16)
+               return NULL;
+
+       return pkg_set_raw(pkg, PKG_MD5SUM, p, len);
+}
+
+char *pkg_get_sha256(const pkg_t *pkg)
+{
+       char *p = pkg_get_raw(pkg, PKG_SHA256SUM);
+
+       if (!p)
+               return NULL;
+
+       return checksum_bin2hex(p, 32);
+}
+
+char *pkg_set_sha256(pkg_t *pkg, const char *cksum)
 {
-    int i;
-    for (i = 0; i < depends->possibility_count; i++)
-    {
-        depend_t *d;
-        d = depends->possibilities[i];
-        free (d->version);
-        free (d);
-    }
-    free (depends->possibilities);
+       size_t len;
+       char *p = checksum_hex2bin(cksum, &len);
+
+       if (!p || len != 32)
+               return NULL;
+
+       return pkg_set_raw(pkg, PKG_SHA256SUM, p, len);
 }
 
-void
-pkg_deinit(pkg_t *pkg)
+
+static void compound_depend_deinit(compound_depend_t * depends)
 {
        int i;
+       for (i = 0; i < depends->possibility_count; i++) {
+               depend_t *d;
+               d = depends->possibilities[i];
+               free(d->version);
+               free(d);
+       }
+       free(depends->possibilities);
+}
+
+void pkg_deinit(pkg_t * pkg)
+{
+       int rem;
+       struct blob_attr *cur;
+       compound_depend_t *deps, *dep;
+       void *ptr;
 
        if (pkg->name)
                free(pkg->name);
        pkg->name = NULL;
 
-       pkg->epoch = 0;
-
-       if (pkg->version)
-               free(pkg->version);
-       pkg->version = NULL;
-       /* revision shares storage with version, so don't free */
-       pkg->revision = NULL;
-
        /* owned by opkg_conf_t */
        pkg->dest = NULL;
        /* owned by opkg_conf_t */
        pkg->src = NULL;
 
-       if (pkg->architecture)
-               free(pkg->architecture);
-       pkg->architecture = NULL;
-
-       if (pkg->maintainer)
-               free(pkg->maintainer);
-       pkg->maintainer = NULL;
-
-       if (pkg->section)
-               free(pkg->section);
-       pkg->section = NULL;
-
-       if (pkg->description)
-               free(pkg->description);
-       pkg->description = NULL;
-       
        pkg->state_want = SW_UNKNOWN;
        pkg->state_flag = SF_OK;
        pkg->state_status = SS_NOT_INSTALLED;
 
-       active_list_clear(&pkg->list);
+       blob_for_each_attr(cur, pkg->blob.head, rem) {
+               switch (blob_id(cur)) {
+               case PKG_DEPENDS:
+               case PKG_CONFLICTS:
+                       deps = pkg_get_ptr(pkg, blob_id(cur));
 
-       if (pkg->replaces)
-               free (pkg->replaces);
-       pkg->replaces = NULL;
+                       if (deps) {
+                               for (dep = deps; dep->type; dep++)
+                                       compound_depend_deinit(dep);
 
-       if (pkg->depends) {
-               int count = pkg->pre_depends_count
-                               + pkg->depends_count
-                               + pkg->recommends_count
-                               + pkg->suggests_count;
-
-               for (i=0; i<count; i++)
-                       compound_depend_deinit (&pkg->depends[i]);
-               free (pkg->depends);
-       }
+                               free(deps);
+                       }
 
-       if (pkg->conflicts) {
-               for (i=0; i<pkg->conflicts_count; i++)
-                       compound_depend_deinit (&pkg->conflicts[i]);
-               free (pkg->conflicts);
-       }
+                       pkg_set_ptr(pkg, blob_id(cur), NULL);
+                       break;
 
-       if (pkg->provides)
-               free (pkg->provides);
-
-       pkg->pre_depends_count = 0;
-       pkg->provides_count = 0;
-       
-       if (pkg->filename)
-               free(pkg->filename);
-       pkg->filename = NULL;
-       
-       if (pkg->local_filename)
-               free(pkg->local_filename);
-       pkg->local_filename = NULL;
-
-     /* CLEANUP: It'd be nice to pullin the cleanup function from
-       opkg_install.c here. See comment in
-       opkg_install.c:cleanup_temporary_files */
-       if (pkg->tmp_unpack_dir)
-               free(pkg->tmp_unpack_dir);
-       pkg->tmp_unpack_dir = NULL;
-
-       if (pkg->md5sum)
-               free(pkg->md5sum);
-       pkg->md5sum = NULL;
+               case PKG_REPLACES:
+               case PKG_PROVIDES:
+                       ptr = pkg_get_ptr(pkg, blob_id(cur));
 
-#if defined HAVE_SHA256
-       if (pkg->sha256sum)
-               free(pkg->sha256sum);
-       pkg->sha256sum = NULL;
-#endif
+                       if (ptr)
+                               free(ptr);
 
-       if (pkg->size)
-               free(pkg->size);
-       pkg->size = NULL;
+                       pkg_set_ptr(pkg, blob_id(cur), NULL);
+                       break;
 
-       if (pkg->installed_size)
-               free(pkg->installed_size);
-       pkg->installed_size = NULL;
+               case PKG_CONFFILES:
+                       ptr = pkg_get_ptr(pkg, blob_id(cur));
 
-       if (pkg->priority)
-               free(pkg->priority);
-       pkg->priority = NULL;
+                       if (ptr) {
+                               conffile_list_deinit(ptr);
+                               free(ptr);
+                       }
 
-       if (pkg->source)
-               free(pkg->source);
-       pkg->source = NULL;
+                       pkg_set_ptr(pkg, blob_id(cur), NULL);
+                       break;
+               }
+       }
 
-       conffile_list_deinit(&pkg->conffiles);
+       //conffile_list_deinit(&pkg->conffiles);
 
        /* XXX: QUESTION: Is forcing this to 1 correct? I suppose so,
-       since if they are calling deinit, they should know. Maybe do an
-       assertion here instead? */
+          since if they are calling deinit, they should know. Maybe do an
+          assertion here instead? */
        pkg->installed_files_ref_cnt = 1;
        pkg_free_installed_files(pkg);
        pkg->essential = 0;
 
-       if (pkg->tags)
-               free (pkg->tags);
-       pkg->tags = NULL;
+       blob_buf_free(&pkg->blob);
 }
 
-int
-pkg_init_from_file(opkg_conf_t *conf, pkg_t *pkg, const char *filename)
+int pkg_init_from_file(pkg_t * pkg, const char *filename)
 {
        int fd, err = 0;
        FILE *control_file;
-       char *control_path;
+       char *control_path, *tmp;
 
        pkg_init(pkg);
 
-       pkg->local_filename = xstrdup(filename);
+       if (!(pkg->state_flag & SF_NEED_DETAIL)) {
+               opkg_msg(DEBUG, "applying abpkg flag to %s\n", filename);
+               pkg->state_flag |= SF_NEED_DETAIL;
+       }
+
+       pkg_set_string(pkg, PKG_LOCAL_FILENAME, filename);
 
-       sprintf_alloc(&control_path, "%s/%s.control.XXXXXX", 
-                        conf->tmp_dir,
-                        basename(filename));
+       tmp = xstrdup(filename);
+       sprintf_alloc(&control_path, "%s/%s.control.XXXXXX",
+                     conf->tmp_dir, basename(tmp));
+       free(tmp);
        fd = mkstemp(control_path);
        if (fd == -1) {
-               perror_msg("%s: mkstemp(%s)", __FUNCTION__, control_path);
+               opkg_perror(ERROR, "Failed to make temp file %s", control_path);
                err = -1;
                goto err0;
        }
 
        control_file = fdopen(fd, "r+");
        if (control_file == NULL) {
-               perror_msg("%s: fdopen", __FUNCTION__, control_path);
+               opkg_perror(ERROR, "Failed to fdopen %s", control_path);
                close(fd);
                err = -1;
                goto err1;
        }
 
        err = pkg_extract_control_file_to_stream(pkg, control_file);
-       if (err)
+       if (err) {
+               opkg_msg(ERROR, "Failed to extract control file from %s.\n",
+                        filename);
                goto err2;
+       }
 
        rewind(control_file);
 
-       if (pkg_parse_from_stream(pkg, control_file, PFM_ALL))
+       if ((err = pkg_parse_from_stream(pkg, control_file, 0))) {
+               if (err == 1) {
+                       opkg_msg(ERROR, "Malformed package file %s.\n",
+                                filename);
+               }
                err = -1;
+       }
 
 err2:
        fclose(control_file);
@@ -328,210 +400,169 @@ err0:
 }
 
 /* Merge any new information in newpkg into oldpkg */
-/* XXX: CLEANUP: This function shouldn't actually modify anything in
-   newpkg, but should leave it usable. This rework is so that
-   pkg_hash_insert doesn't clobber the pkg that you pass into it. */
-/* 
- * uh, i thought that i had originally written this so that it took 
- * two pkgs and returned a new one?  we can do that again... -sma
- */
-int
-pkg_merge(pkg_t *oldpkg, pkg_t *newpkg, int set_status)
+int pkg_merge(pkg_t * oldpkg, pkg_t * newpkg)
 {
-     if (oldpkg == newpkg) {
-         return 0;
-     }
-
-     if (!oldpkg->auto_installed)
-         oldpkg->auto_installed = newpkg->auto_installed;
-
-     if (!oldpkg->src)
-         oldpkg->src = newpkg->src;
-     if (!oldpkg->dest)
-         oldpkg->dest = newpkg->dest;
-     if (!oldpkg->architecture)
-         oldpkg->architecture = xstrdup(newpkg->architecture);
-     if (!oldpkg->arch_priority)
-         oldpkg->arch_priority = newpkg->arch_priority;
-     if (!oldpkg->section)
-         oldpkg->section = xstrdup(newpkg->section);
-     if(!oldpkg->maintainer)
-         oldpkg->maintainer = xstrdup(newpkg->maintainer);
-     if(!oldpkg->description)
-         oldpkg->description = xstrdup(newpkg->description);
-     if (set_status) {
-         /* merge the state_flags from the new package */
-         oldpkg->state_want = newpkg->state_want;
-         oldpkg->state_status = newpkg->state_status;
-         oldpkg->state_flag = newpkg->state_flag;
-     } else {
-         if (oldpkg->state_want == SW_UNKNOWN)
-              oldpkg->state_want = newpkg->state_want;
-         if (oldpkg->state_status == SS_NOT_INSTALLED)
-              oldpkg->state_status = newpkg->state_status;
-         oldpkg->state_flag |= newpkg->state_flag;
-     }
-
-     if (!oldpkg->depends_count && !oldpkg->pre_depends_count && !oldpkg->recommends_count && !oldpkg->suggests_count) {
-         oldpkg->depends_count = newpkg->depends_count;
-         newpkg->depends_count = 0;
-
-         oldpkg->depends = newpkg->depends;
-         newpkg->depends = NULL;
-
-         oldpkg->pre_depends_count = newpkg->pre_depends_count;
-         newpkg->pre_depends_count = 0;
-
-         oldpkg->recommends_count = newpkg->recommends_count;
-         newpkg->recommends_count = 0;
-
-         oldpkg->suggests_count = newpkg->suggests_count;
-         newpkg->suggests_count = 0;
-     }
-
-     if (oldpkg->provides_count <= 1) {
-         oldpkg->provides_count = newpkg->provides_count;
-         newpkg->provides_count = 0;
-
-         if (!oldpkg->provides) {
-               oldpkg->provides = newpkg->provides;
-               newpkg->provides = NULL;
-         }
-     }
-
-     if (!oldpkg->conflicts_count) {
-         oldpkg->conflicts_count = newpkg->conflicts_count;
-         newpkg->conflicts_count = 0;
-
-         oldpkg->conflicts = newpkg->conflicts;
-         newpkg->conflicts = NULL;
-     }
-
-     if (!oldpkg->replaces_count) {
-         oldpkg->replaces_count = newpkg->replaces_count;
-         newpkg->replaces_count = 0;
-
-         oldpkg->replaces = newpkg->replaces;
-         newpkg->replaces = NULL;
-     }
-
-     if (!oldpkg->filename)
-         oldpkg->filename = xstrdup(newpkg->filename);
-     if (!oldpkg->local_filename)
-         oldpkg->local_filename = xstrdup(newpkg->local_filename);
-     if (!oldpkg->tmp_unpack_dir)
-         oldpkg->tmp_unpack_dir = xstrdup(newpkg->tmp_unpack_dir);
-     if (!oldpkg->md5sum)
-         oldpkg->md5sum = xstrdup(newpkg->md5sum);
-#if defined HAVE_SHA256
-     if (!oldpkg->sha256sum)
-         oldpkg->sha256sum = xstrdup(newpkg->sha256sum);
-#endif
-     if (!oldpkg->size)
-         oldpkg->size = xstrdup(newpkg->size);
-     if (!oldpkg->installed_size)
-         oldpkg->installed_size = xstrdup(newpkg->installed_size);
-     if (!oldpkg->priority)
-         oldpkg->priority = xstrdup(newpkg->priority);
-     if (!oldpkg->source)
-         oldpkg->source = xstrdup(newpkg->source);
-     if (nv_pair_list_empty(&oldpkg->conffiles)){
-         list_splice_init(&newpkg->conffiles.head, &oldpkg->conffiles.head);
-         conffile_list_init(&newpkg->conffiles);
-     }
-     if (!oldpkg->installed_files){
-         oldpkg->installed_files = newpkg->installed_files;
-         oldpkg->installed_files_ref_cnt = newpkg->installed_files_ref_cnt;
-         newpkg->installed_files = NULL;
-     }
-     if (!oldpkg->essential)
-         oldpkg->essential = newpkg->essential;
-
-     return 0;
+       abstract_pkg_t **ab;
+
+       if (oldpkg == newpkg) {
+               return 0;
+       }
+
+       if (!oldpkg->auto_installed)
+               oldpkg->auto_installed = newpkg->auto_installed;
+
+       if (!oldpkg->src)
+               oldpkg->src = newpkg->src;
+       if (!oldpkg->dest)
+               oldpkg->dest = newpkg->dest;
+       if (!oldpkg->arch_index)
+               oldpkg->arch_index = newpkg->arch_index;
+       if (!pkg_get_string(oldpkg, PKG_SECTION))
+               pkg_set_string(oldpkg, PKG_SECTION, pkg_get_string(newpkg, PKG_SECTION));
+       if (!pkg_get_string(oldpkg, PKG_MAINTAINER))
+               pkg_set_string(oldpkg, PKG_MAINTAINER, pkg_get_string(newpkg, PKG_MAINTAINER));
+       if (!pkg_get_string(oldpkg, PKG_DESCRIPTION))
+               pkg_set_string(oldpkg, PKG_DESCRIPTION, pkg_get_string(newpkg, PKG_DESCRIPTION));
+
+       if (!pkg_get_ptr(oldpkg, PKG_DEPENDS)) {
+               pkg_set_ptr(oldpkg, PKG_DEPENDS, pkg_get_ptr(newpkg, PKG_DEPENDS));
+               pkg_set_ptr(newpkg, PKG_DEPENDS, NULL);
+       }
+
+       ab = pkg_get_ptr(oldpkg, PKG_PROVIDES);
+
+       if (!ab || !ab[0] || !ab[1]) {
+               pkg_set_ptr(oldpkg, PKG_PROVIDES, pkg_get_ptr(newpkg, PKG_PROVIDES));
+               pkg_set_ptr(newpkg, PKG_PROVIDES, NULL);
+
+               if (ab)
+                       free(ab);
+       }
+
+       if (!pkg_get_ptr(oldpkg, PKG_CONFLICTS)) {
+               pkg_set_ptr(oldpkg, PKG_CONFLICTS, pkg_get_ptr(newpkg, PKG_CONFLICTS));
+               pkg_set_ptr(newpkg, PKG_CONFLICTS, NULL);
+       }
+
+       if (!pkg_get_ptr(oldpkg, PKG_REPLACES)) {
+               pkg_set_ptr(oldpkg, PKG_REPLACES, pkg_get_ptr(newpkg, PKG_REPLACES));
+               pkg_set_ptr(newpkg, PKG_REPLACES, NULL);
+       }
+
+       if (!pkg_get_string(oldpkg, PKG_FILENAME))
+               pkg_set_string(oldpkg, PKG_FILENAME, pkg_get_string(newpkg, PKG_FILENAME));
+       if (!pkg_get_string(oldpkg, PKG_LOCAL_FILENAME))
+               pkg_set_string(oldpkg, PKG_LOCAL_FILENAME, pkg_get_string(newpkg, PKG_LOCAL_FILENAME));
+       if (!pkg_get_string(oldpkg, PKG_TMP_UNPACK_DIR))
+               pkg_set_string(oldpkg, PKG_TMP_UNPACK_DIR, pkg_get_string(newpkg, PKG_TMP_UNPACK_DIR));
+       if (!pkg_get_md5(oldpkg))
+               pkg_set_md5(oldpkg, pkg_get_md5(newpkg));
+       if (!pkg_get_sha256(oldpkg))
+               pkg_set_sha256(oldpkg, pkg_get_sha256(newpkg));
+       if (!pkg_get_int(oldpkg, PKG_SIZE))
+               pkg_set_int(oldpkg, PKG_SIZE, pkg_get_int(newpkg, PKG_SIZE));
+       if (!pkg_get_int(oldpkg, PKG_INSTALLED_SIZE))
+               pkg_set_int(oldpkg, PKG_INSTALLED_SIZE, pkg_get_int(newpkg, PKG_INSTALLED_SIZE));
+       if (!pkg_get_string(oldpkg, PKG_PRIORITY))
+               pkg_set_string(oldpkg, PKG_PRIORITY, pkg_get_string(newpkg, PKG_PRIORITY));
+       if (!pkg_get_string(oldpkg, PKG_SOURCE))
+               pkg_set_string(oldpkg, PKG_SOURCE, pkg_get_string(newpkg, PKG_SOURCE));
+
+       if (!pkg_get_ptr(oldpkg, PKG_CONFFILES)) {
+               pkg_set_ptr(oldpkg, PKG_CONFFILES, pkg_get_ptr(newpkg, PKG_CONFFILES));
+               pkg_set_ptr(newpkg, PKG_CONFFILES, NULL);
+       }
+
+       if (!oldpkg->installed_files) {
+               oldpkg->installed_files = newpkg->installed_files;
+               oldpkg->installed_files_ref_cnt =
+                   newpkg->installed_files_ref_cnt;
+               newpkg->installed_files = NULL;
+       }
+
+       if (!oldpkg->essential)
+               oldpkg->essential = newpkg->essential;
+
+       return 0;
 }
 
-static void
-abstract_pkg_init(abstract_pkg_t *ab_pkg)
+static void abstract_pkg_init(abstract_pkg_t * ab_pkg)
 {
-     ab_pkg->provided_by = abstract_pkg_vec_alloc();
-     ab_pkg->dependencies_checked = 0;
-     ab_pkg->state_status = SS_NOT_INSTALLED;
+       ab_pkg->provided_by = abstract_pkg_vec_alloc();
+       ab_pkg->dependencies_checked = 0;
+       ab_pkg->state_status = SS_NOT_INSTALLED;
 }
 
-abstract_pkg_t *
-abstract_pkg_new(void)
+abstract_pkg_t *abstract_pkg_new(void)
 {
-     abstract_pkg_t * ab_pkg;
+       abstract_pkg_t *ab_pkg;
 
-     ab_pkg = xcalloc(1, sizeof(abstract_pkg_t));
-     abstract_pkg_init(ab_pkg);
+       ab_pkg = xcalloc(1, sizeof(abstract_pkg_t));
+       abstract_pkg_init(ab_pkg);
 
-     return ab_pkg;
+       return ab_pkg;
 }
 
-void
-set_flags_from_control(opkg_conf_t *conf, pkg_t *pkg){
-     char *file_name;
-     FILE *fp;
+void set_flags_from_control(pkg_t * pkg)
+{
+       char *file_name;
+       FILE *fp;
 
-     sprintf_alloc(&file_name,"%s/%s.control", pkg->dest->info_dir, pkg->name);
+       sprintf_alloc(&file_name, "%s/%s.control", pkg->dest->info_dir,
+                     pkg->name);
 
-     fp = fopen(file_name, "r");
-     if (fp == NULL) {
-            opkg_message(conf, OPKG_ERROR, "%s: fopen(%s): %s\n",
-                            __FUNCTION__, file_name, strerror(errno));
-            free(file_name);
-            return;
-     }
+       fp = fopen(file_name, "r");
+       if (fp == NULL) {
+               opkg_perror(ERROR, "Failed to open %s", file_name);
+               free(file_name);
+               return;
+       }
 
-     free(file_name);
+       free(file_name);
 
-     if (pkg_parse_from_stream(pkg, fp, PFM_ESSENTIAL)) {
-        opkg_message(conf, OPKG_DEBUG, "unable to read control file for %s. May be empty\n", pkg->name);
-     }
+       if (pkg_parse_from_stream(pkg, fp, PFM_ALL ^ PFM_ESSENTIAL)) {
+               opkg_msg(DEBUG,
+                        "Unable to read control file for %s. May be empty.\n",
+                        pkg->name);
+       }
 
-     fclose(fp);
+       fclose(fp);
 
-     return;
+       return;
 }
 
-static char *
-pkg_state_want_to_str(pkg_state_want_t sw)
+static const char *pkg_state_want_to_str(pkg_state_want_t sw)
 {
-     int i;
+       int i;
 
-     for (i=0; i < ARRAY_SIZE(pkg_state_want_map); i++) {
-         if (pkg_state_want_map[i].value == sw) {
-              return pkg_state_want_map[i].str;
-         }
-     }
+       for (i = 0; i < ARRAY_SIZE(pkg_state_want_map); i++) {
+               if (pkg_state_want_map[i].value == sw) {
+                       return pkg_state_want_map[i].str;
+               }
+       }
 
-     fprintf(stderr, "%s: ERROR: Illegal value for state_want: %d\n",
-            __FUNCTION__, sw);
-     return "<STATE_WANT_UNKNOWN>";
+       opkg_msg(ERROR, "Internal error: state_want=%d\n", sw);
+       return "<STATE_WANT_UNKNOWN>";
 }
 
-pkg_state_want_t
-pkg_state_want_from_str(char *str)
+pkg_state_want_t pkg_state_want_from_str(char *str)
 {
-     int i;
+       int i;
 
-     for (i=0; i < ARRAY_SIZE(pkg_state_want_map); i++) {
-         if (strcmp(str, pkg_state_want_map[i].str) == 0) {
-              return pkg_state_want_map[i].value;
-         }
-     }
+       for (i = 0; i < ARRAY_SIZE(pkg_state_want_map); i++) {
+               if (strcmp(str, pkg_state_want_map[i].str) == 0) {
+                       return pkg_state_want_map[i].value;
+               }
+       }
 
-     fprintf(stderr, "%s: ERROR: Illegal value for state_want string: %s\n",
-            __FUNCTION__, str);
-     return SW_UNKNOWN;
+       opkg_msg(ERROR, "Internal error: state_want=%s\n", str);
+       return SW_UNKNOWN;
 }
 
-static char *
-pkg_state_flag_to_str(pkg_state_flag_t sf)
+static char *pkg_state_flag_to_str(pkg_state_flag_t sf)
 {
        int i;
-       int len;
+       unsigned int len;
        char *str;
 
        /* clear the temporary flags before converting to string */
@@ -541,15 +572,15 @@ pkg_state_flag_to_str(pkg_state_flag_t sf)
                return xstrdup("ok");
 
        len = 0;
-       for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
+       for (i = 0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
                if (sf & pkg_state_flag_map[i].value)
                        len += strlen(pkg_state_flag_map[i].str) + 1;
        }
 
-       str = xmalloc(len+1);
+       str = xmalloc(len + 1);
        str[0] = '\0';
 
-       for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
+       for (i = 0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
                if (sf & pkg_state_flag_map[i].value) {
                        strncat(str, pkg_state_flag_map[i].str, len);
                        strncat(str, ",", len);
@@ -557,312 +588,340 @@ pkg_state_flag_to_str(pkg_state_flag_t sf)
        }
 
        len = strlen(str);
-       str[len-1] = '\0'; /* squash last comma */
+       str[len - 1] = '\0';    /* squash last comma */
 
        return str;
 }
 
-pkg_state_flag_t
-pkg_state_flag_from_str(const char *str)
+pkg_state_flag_t pkg_state_flag_from_str(const char *str)
 {
-     int i;
-     int sf = SF_OK;
-
-     if (strcmp(str, "ok") == 0) {
-         return SF_OK;
-     }
-     for (i=0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
-         const char *sfname = pkg_state_flag_map[i].str;
-         int sfname_len = strlen(sfname);
-         if (strncmp(str, sfname, sfname_len) == 0) {
-              sf |= pkg_state_flag_map[i].value;
-              str += sfname_len;
-              if (str[0] == ',') {
-                   str++;
-              } else {
-                   break;
-              }
-         }
-     }
-
-     return sf;
+       int i;
+       int sf = SF_OK;
+       const char *sfname;
+       unsigned int sfname_len;
+
+       if (strcmp(str, "ok") == 0) {
+               return SF_OK;
+       }
+       for (i = 0; i < ARRAY_SIZE(pkg_state_flag_map); i++) {
+               sfname = pkg_state_flag_map[i].str;
+               sfname_len = strlen(sfname);
+               if (strncmp(str, sfname, sfname_len) == 0) {
+                       sf |= pkg_state_flag_map[i].value;
+                       str += sfname_len;
+                       if (str[0] == ',') {
+                               str++;
+                       } else {
+                               break;
+                       }
+               }
+       }
+
+       return sf;
 }
 
-static char *
-pkg_state_status_to_str(pkg_state_status_t ss)
+static const char *pkg_state_status_to_str(pkg_state_status_t ss)
 {
-     int i;
+       int i;
 
-     for (i=0; i < ARRAY_SIZE(pkg_state_status_map); i++) {
-         if (pkg_state_status_map[i].value == ss) {
-              return pkg_state_status_map[i].str;
-         }
-     }
+       for (i = 0; i < ARRAY_SIZE(pkg_state_status_map); i++) {
+               if (pkg_state_status_map[i].value == ss) {
+                       return pkg_state_status_map[i].str;
+               }
+       }
 
-     fprintf(stderr, "%s: ERROR: Illegal value for state_status: %d\n",
-            __FUNCTION__, ss);
-     return "<STATE_STATUS_UNKNOWN>";
+       opkg_msg(ERROR, "Internal error: state_status=%d\n", ss);
+       return "<STATE_STATUS_UNKNOWN>";
 }
 
-pkg_state_status_t
-pkg_state_status_from_str(const char *str)
+pkg_state_status_t pkg_state_status_from_str(const char *str)
 {
-     int i;
+       int i;
 
-     for (i=0; i < ARRAY_SIZE(pkg_state_status_map); i++) {
-         if (strcmp(str, pkg_state_status_map[i].str) == 0) {
-              return pkg_state_status_map[i].value;
-         }
-     }
+       for (i = 0; i < ARRAY_SIZE(pkg_state_status_map); i++) {
+               if (strcmp(str, pkg_state_status_map[i].str) == 0) {
+                       return pkg_state_status_map[i].value;
+               }
+       }
 
-     fprintf(stderr, "%s: ERROR: Illegal value for state_status string: %s\n",
-            __FUNCTION__, str);
-     return SS_NOT_INSTALLED;
+       opkg_msg(ERROR, "Internal error: state_status=%s\n", str);
+       return SS_NOT_INSTALLED;
 }
 
-void
-pkg_formatted_field(FILE *fp, pkg_t *pkg, const char *field)
+void pkg_formatted_field(FILE * fp, pkg_t * pkg, const char *field)
 {
-     int i, j;
-     char *str;
-     int depends_count = pkg->pre_depends_count +
-                        pkg->depends_count +
-                        pkg->recommends_count +
-                        pkg->suggests_count;
-
-     if (strlen(field) < PKG_MINIMUM_FIELD_NAME_LEN) {
-         goto UNKNOWN_FMT_FIELD;
-     }
-
-     switch (field[0])
-     {
-     case 'a':
-     case 'A':
-         if (strcasecmp(field, "Architecture") == 0) {
-              if (pkg->architecture) {
-                   fprintf(fp, "Architecture: %s\n", pkg->architecture);
-              }
-         } else if (strcasecmp(field, "Auto-Installed") == 0) {
-               if (pkg->auto_installed)
-                   fprintf(fp, "Auto-Installed: yes\n");
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 'c':
-     case 'C':
-         if (strcasecmp(field, "Conffiles") == 0) {
-              conffile_list_elt_t *iter;
-
-              if (nv_pair_list_empty(&pkg->conffiles))
-                   return;
-
-               fprintf(fp, "Conffiles:\n");
-              for (iter = nv_pair_list_first(&pkg->conffiles); iter; iter = nv_pair_list_next(&pkg->conffiles, iter)) {
-                   if (((conffile_t *)iter->data)->name && ((conffile_t *)iter->data)->value) {
-                         fprintf(fp, " %s %s\n", 
-                                 ((conffile_t *)iter->data)->name, 
-                                 ((conffile_t *)iter->data)->value);
-                   }
-              }
-         } else if (strcasecmp(field, "Conflicts") == 0) {
-              struct depend *cdep;
-              if (pkg->conflicts_count) {
-                    fprintf(fp, "Conflicts:");
-                   for(i = 0; i < pkg->conflicts_count; i++) {
-                       cdep = pkg->conflicts[i].possibilities[0];
-                        fprintf(fp, "%s %s", i == 0 ? "" : ",",
-                               cdep->pkg->name);
-                       if (cdep->version) {
-                               fprintf(fp, " (%s%s)",
-                                       constraint_to_str(cdep->constraint),
-                                       cdep->version);
+       int i, j;
+       char *str;
+       const char *p;
+       compound_depend_t *dep;
+       abstract_pkg_t **ab_pkg;
+
+       if (strlen(field) < PKG_MINIMUM_FIELD_NAME_LEN) {
+               goto UNKNOWN_FMT_FIELD;
+       }
+
+       switch (field[0]) {
+       case 'a':
+       case 'A':
+               if (strcasecmp(field, "Architecture") == 0) {
+                       p = pkg_get_architecture(pkg);
+                       if (p) {
+                               fprintf(fp, "Architecture: %s\n",
+                                       p);
+                       }
+               } else if (strcasecmp(field, "Auto-Installed") == 0) {
+                       if (pkg->auto_installed)
+                               fprintf(fp, "Auto-Installed: yes\n");
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 'c':
+       case 'C':
+               if (strcasecmp(field, "Conffiles") == 0) {
+                       conffile_list_t *cl;
+                       conffile_list_elt_t *iter;
+
+                       cl = pkg_get_ptr(pkg, PKG_CONFFILES);
+
+                       if (!cl || nv_pair_list_empty(cl))
+                               return;
+
+                       fprintf(fp, "Conffiles:\n");
+                       for (iter = nv_pair_list_first(cl); iter;
+                            iter = nv_pair_list_next(cl, iter)) {
+                               if (((conffile_t *) iter->data)->name
+                                   && ((conffile_t *) iter->data)->value) {
+                                       fprintf(fp, " %s %s\n",
+                                               ((conffile_t *) iter->data)->
+                                               name,
+                                               ((conffile_t *) iter->data)->
+                                               value);
+                               }
+                       }
+               } else if (strcasecmp(field, "Conflicts") == 0) {
+                       struct depend *cdep;
+                       compound_depend_t *deps, *dep;
+                       deps = pkg_get_ptr(pkg, PKG_CONFLICTS);
+                       if (deps) {
+                               fprintf(fp, "Conflicts:");
+                               for (i = 0, dep = deps; dep->type; dep++, i++) {
+                                       cdep = dep->possibilities[0];
+                                       fprintf(fp, "%s %s", i == 0 ? "" : ",",
+                                               cdep->pkg->name);
+                                       if (cdep->version) {
+                                               fprintf(fp, " (%s%s)",
+                                                       constraint_to_str(cdep->
+                                                                         constraint),
+                                                       cdep->version);
+                                       }
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 'd':
+       case 'D':
+               if (strcasecmp(field, "Depends") == 0) {
+                       dep = pkg_get_depends(pkg, DEPEND);
+                       if (dep) {
+                               fprintf(fp, "Depends:");
+                               for (i = 0, j = 0; dep && dep->type; i++, dep++) {
+                                       if (dep->type != DEPEND)
+                                               continue;
+                                       str = pkg_depend_str(pkg, i);
+                                       fprintf(fp, "%s %s", j == 0 ? "" : ",",
+                                               str);
+                                       free(str);
+                                       j++;
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else if (strcasecmp(field, "Description") == 0) {
+                       p = pkg_get_string(pkg, PKG_DESCRIPTION);
+                       if (p) {
+                               fprintf(fp, "Description: %s\n",
+                                       p);
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 'e':
+       case 'E':
+               if (pkg->essential) {
+                       fprintf(fp, "Essential: yes\n");
+               }
+               break;
+       case 'f':
+       case 'F':
+               p = pkg_get_string(pkg, PKG_FILENAME);
+               if (p) {
+                       fprintf(fp, "Filename: %s\n", p);
+               }
+               break;
+       case 'i':
+       case 'I':
+               if (strcasecmp(field, "Installed-Size") == 0) {
+                       fprintf(fp, "Installed-Size: %lu\n",
+                               (unsigned long) pkg_get_int(pkg, PKG_INSTALLED_SIZE));
+               } else if (strcasecmp(field, "Installed-Time") == 0) {
+                       i = pkg_get_int(pkg, PKG_INSTALLED_TIME);
+                       if (i) {
+                               fprintf(fp, "Installed-Time: %lu\n",
+                                       (unsigned long) i);
+                       }
+               }
+               break;
+       case 'm':
+       case 'M':
+               if (strcasecmp(field, "Maintainer") == 0) {
+                       p = pkg_get_string(pkg, PKG_MAINTAINER);
+                       if (p) {
+                               fprintf(fp, "Maintainer: %s\n", p);
+                       }
+               } else if (strcasecmp(field, "MD5sum") == 0) {
+                       p = pkg_get_md5(pkg);
+                       if (p) {
+                               fprintf(fp, "MD5Sum: %s\n", p);
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 'p':
+       case 'P':
+               if (strcasecmp(field, "Package") == 0) {
+                       fprintf(fp, "Package: %s\n", pkg->name);
+               } else if (strcasecmp(field, "Priority") == 0) {
+                       fprintf(fp, "Priority: %s\n", pkg_get_string(pkg, PKG_PRIORITY));
+               } else if (strcasecmp(field, "Provides") == 0) {
+                       ab_pkg = pkg_get_ptr(pkg, PKG_PROVIDES);
+                       if (ab_pkg && ab_pkg[0] && ab_pkg[1]) {
+                               fprintf(fp, "Provides:");
+                               for (i = 1; ab_pkg[i]; i++) {
+                                       fprintf(fp, "%s %s", i == 1 ? "" : ",",
+                                               ab_pkg[i]->name);
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 'r':
+       case 'R':
+               if (strcasecmp(field, "Replaces") == 0) {
+                       ab_pkg = pkg_get_ptr(pkg, PKG_REPLACES);
+                       if (ab_pkg && *ab_pkg) {
+                               fprintf(fp, "Replaces:");
+                               for (i = 0; *ab_pkg; i++, ab_pkg++) {
+                                       fprintf(fp, "%s %s", i == 0 ? "" : ",",
+                                               (*ab_pkg)->name);
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else if (strcasecmp(field, "Recommends") == 0) {
+                       dep = pkg_get_depends(pkg, RECOMMEND);
+                       if (dep) {
+                               fprintf(fp, "Recommends:");
+                               for (j = 0, i = 0; dep && dep->type; i++, dep++) {
+                                       if (dep->type != RECOMMEND)
+                                               continue;
+                                       str = pkg_depend_str(pkg, i);
+                                       fprintf(fp, "%s %s", j == 0 ? "" : ",",
+                                               str);
+                                       free(str);
+                                       j++;
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 's':
+       case 'S':
+               if (strcasecmp(field, "Section") == 0) {
+                       p = pkg_get_string(pkg, PKG_SECTION);
+                       if (p) {
+                               fprintf(fp, "Section: %s\n", p);
                        }
-                    }
-                    fprintf(fp, "\n");
-              }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 'd':
-     case 'D':
-         if (strcasecmp(field, "Depends") == 0) {
-              if (pkg->depends_count) {
-                    fprintf(fp, "Depends:");
-                   for (j=0, i=0; i<depends_count; i++) {
-                       if (pkg->depends[i].type != DEPEND)
-                               continue;
-                       str = pkg_depend_str(pkg, i);
-                       fprintf(fp, "%s %s", j == 0 ? "" : ",", str);
-                       free(str);
-                       j++;
-                    }
-                   fprintf(fp, "\n");
-              }
-         } else if (strcasecmp(field, "Description") == 0) {
-              if (pkg->description) {
-                   fprintf(fp, "Description: %s\n", pkg->description);
-              }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-          break;
-     case 'e':
-     case 'E':
-         if (pkg->essential) {
-              fprintf(fp, "Essential: yes\n");
-         }
-         break;
-     case 'f':
-     case 'F':
-         if (pkg->filename) {
-              fprintf(fp, "Filename: %s\n", pkg->filename);
-         }
-         break;
-     case 'i':
-     case 'I':
-         if (strcasecmp(field, "Installed-Size") == 0) {
-               fprintf(fp, "Installed-Size: %s\n", pkg->installed_size);
-         } else if (strcasecmp(field, "Installed-Time") == 0 && pkg->installed_time) {
-               fprintf(fp, "Installed-Time: %lu\n", pkg->installed_time);
-         }
-         break;
-     case 'm':
-     case 'M':
-         if (strcasecmp(field, "Maintainer") == 0) {
-              if (pkg->maintainer) {
-                   fprintf(fp, "maintainer: %s\n", pkg->maintainer);
-              }
-         } else if (strcasecmp(field, "MD5sum") == 0) {
-              if (pkg->md5sum) {
-                   fprintf(fp, "MD5Sum: %s\n", pkg->md5sum);
-              }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 'p':
-     case 'P':
-         if (strcasecmp(field, "Package") == 0) {
-               fprintf(fp, "Package: %s\n", pkg->name);
-         } else if (strcasecmp(field, "Priority") == 0) {
-               fprintf(fp, "Priority: %s\n", pkg->priority);
-         } else if (strcasecmp(field, "Provides") == 0) {
-              if (pkg->provides_count) {
-                  fprintf(fp, "Provides:");
-                 for(i = 1; i < pkg->provides_count; i++) {
-                      fprintf(fp, "%s %s", i == 1 ? "" : ",",
-                                     pkg->provides[i]->name);
-                  }
-                  fprintf(fp, "\n");
-               }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 'r':
-     case 'R':
-         if (strcasecmp (field, "Replaces") == 0) {
-              if (pkg->replaces_count) {
-                    fprintf(fp, "Replaces:");
-                   for (i = 0; i < pkg->replaces_count; i++) {
-                        fprintf(fp, "%s %s", i == 0 ? "" : ",",
-                                       pkg->replaces[i]->name);
-                    }
-                    fprintf(fp, "\n");
-              }
-         } else if (strcasecmp (field, "Recommends") == 0) {
-              if (pkg->recommends_count) {
-                    fprintf(fp, "Recommends:");
-                   for (j=0, i=0; i<depends_count; i++) {
-                       if (pkg->depends[i].type != RECOMMEND)
-                               continue;
-                       str = pkg_depend_str(pkg, i);
-                       fprintf(fp, "%s %s", j == 0 ? "" : ",", str);
-                       free(str);
-                       j++;
-                    }
-                    fprintf(fp, "\n");
-              }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 's':
-     case 'S':
-         if (strcasecmp(field, "Section") == 0) {
-              if (pkg->section) {
-                   fprintf(fp, "Section: %s\n", pkg->section);
-              }
 #if defined HAVE_SHA256
-         } else if (strcasecmp(field, "SHA256sum") == 0) {
-              if (pkg->sha256sum) {
-                   fprintf(fp, "SHA256sum: %s\n", pkg->sha256sum);
-              }
+               } else if (strcasecmp(field, "SHA256sum") == 0) {
+                       p = pkg_get_string(pkg, PKG_SHA256SUM);
+                       if (p) {
+                               fprintf(fp, "SHA256sum: %s\n", p);
+                       }
 #endif
-         } else if (strcasecmp(field, "Size") == 0) {
-              if (pkg->size) {
-                   fprintf(fp, "Size: %s\n", pkg->size);
-              }
-         } else if (strcasecmp(field, "Source") == 0) {
-              if (pkg->source) {
-                   fprintf(fp, "Source: %s\n", pkg->source);
-               }
-         } else if (strcasecmp(field, "Status") == 0) {
-               char *pflag = pkg_state_flag_to_str(pkg->state_flag);
-               fprintf(fp, "Status: %s %s %s\n",
-                                      pkg_state_want_to_str(pkg->state_want),
-                              pflag,
-                               pkg_state_status_to_str(pkg->state_status));
-               free(pflag);
-         } else if (strcasecmp(field, "Suggests") == 0) {
-              if (pkg->suggests_count) {
-                    fprintf(fp, "Suggests:");
-                   for (j=0, i=0; i<depends_count; i++) {
-                       if (pkg->depends[i].type != SUGGEST)
-                               continue;
-                       str = pkg_depend_str(pkg, i);
-                       fprintf(fp, "%s %s", j == 0 ? "" : ",", str);
-                       free(str);
-                       j++;
-                    }
-                    fprintf(fp, "\n");
-              }
-         } else {
-              goto UNKNOWN_FMT_FIELD;
-         }
-         break;
-     case 't':
-     case 'T':
-         if (strcasecmp(field, "Tags") == 0) {
-              if (pkg->tags) {
-                   fprintf(fp, "Tags: %s\n", pkg->tags);
-              }
-         }
-         break;
-     case 'v':
-     case 'V':
-         {
-              char *version = pkg_version_str_alloc(pkg);
-              if (version == NULL)
-                   return;
-               fprintf(fp, "Version: %s\n", version);
-              free(version);
-          }
-         break;
-     default:
-         goto UNKNOWN_FMT_FIELD;
-     }
-
-     return;
+               } else if (strcasecmp(field, "Size") == 0) {
+                       i = pkg_get_int(pkg, PKG_SIZE);
+                       if (i) {
+                               fprintf(fp, "Size: %lu\n", (unsigned long) i);
+                       }
+               } else if (strcasecmp(field, "Source") == 0) {
+                       p = pkg_get_string(pkg, PKG_SOURCE);
+                       if (p) {
+                               fprintf(fp, "Source: %s\n", p);
+                       }
+               } else if (strcasecmp(field, "Status") == 0) {
+                       char *pflag = pkg_state_flag_to_str(pkg->state_flag);
+                       fprintf(fp, "Status: %s %s %s\n",
+                               pkg_state_want_to_str(pkg->state_want),
+                               pflag,
+                               pkg_state_status_to_str(pkg->state_status));
+                       free(pflag);
+               } else if (strcasecmp(field, "Suggests") == 0) {
+                       dep = pkg_get_depends(pkg, SUGGEST);
+                       if (dep) {
+                               fprintf(fp, "Suggests:");
+                               for (j = 0, i = 0; dep && dep->type; i++, dep++) {
+                                       if (dep->type != SUGGEST)
+                                               continue;
+                                       str = pkg_depend_str(pkg, i);
+                                       fprintf(fp, "%s %s", j == 0 ? "" : ",",
+                                               str);
+                                       free(str);
+                                       j++;
+                               }
+                               fprintf(fp, "\n");
+                       }
+               } else {
+                       goto UNKNOWN_FMT_FIELD;
+               }
+               break;
+       case 't':
+       case 'T':
+               if (strcasecmp(field, "Tags") == 0) {
+                       p = pkg_get_string(pkg, PKG_TAGS);
+                       if (p) {
+                               fprintf(fp, "Tags: %s\n", p);
+                       }
+               }
+               break;
+       case 'v':
+       case 'V':
+               {
+                       char *version = pkg_version_str_alloc(pkg);
+                       if (version == NULL)
+                               return;
+                       fprintf(fp, "Version: %s\n", version);
+                       free(version);
+               }
+               break;
+       default:
+               goto UNKNOWN_FMT_FIELD;
+       }
+
+       return;
 
 UNKNOWN_FMT_FIELD:
-     fprintf(stderr, "%s: ERROR: Unknown field name: %s\n", __FUNCTION__, field);
+       opkg_msg(ERROR, "Internal error: field=%s\n", field);
 }
 
-void
-pkg_formatted_info(FILE *fp, pkg_t *pkg)
+void pkg_formatted_info(FILE * fp, pkg_t * pkg)
 {
        pkg_formatted_field(fp, pkg, "Package");
        pkg_formatted_field(fp, pkg, "Version");
@@ -888,48 +947,27 @@ pkg_formatted_info(FILE *fp, pkg_t *pkg)
        fputs("\n", fp);
 }
 
-void
-pkg_print_status(pkg_t * pkg, FILE * file)
+void pkg_print_status(pkg_t * pkg, FILE * file)
 {
-     if (pkg == NULL) {
-         return;
-     }
-
-     /* XXX: QUESTION: Do we actually want more fields here? The
-       original idea was to save space by installing only what was
-       needed for actual computation, (package, version, status,
-       essential, conffiles). The assumption is that all other fields
-       can be found in th available file.
-
-       But, someone proposed the idea to make it possible to
-       reconstruct a .opk from an installed package, (ie. for beaming
-       from one handheld to another). So, maybe we actually want a few
-       more fields here, (depends, suggests, etc.), so that that would
-       be guaranteed to work even in the absence of more information
-       from the available file.
-
-       28-MAR-03: kergoth and I discussed this yesterday.  We think
-       the essential info needs to be here for all installed packages
-       because they may not appear in the Packages files on various
-       feeds.  Furthermore, one should be able to install from URL or
-       local storage without requiring a Packages file from any feed.
-       -Jamey
-     */
-     pkg_formatted_field(file, pkg, "Package");
-     pkg_formatted_field(file, pkg, "Version");
-     pkg_formatted_field(file, pkg, "Depends");
-     pkg_formatted_field(file, pkg, "Recommends");
-     pkg_formatted_field(file, pkg, "Suggests");
-     pkg_formatted_field(file, pkg, "Provides");
-     pkg_formatted_field(file, pkg, "Replaces");
-     pkg_formatted_field(file, pkg, "Conflicts");
-     pkg_formatted_field(file, pkg, "Status");
-     pkg_formatted_field(file, pkg, "Essential");
-     pkg_formatted_field(file, pkg, "Architecture");
-     pkg_formatted_field(file, pkg, "Conffiles");
-     pkg_formatted_field(file, pkg, "Installed-Time");
-     pkg_formatted_field(file, pkg, "Auto-Installed");
-     fputs("\n", file);
+       if (pkg == NULL) {
+               return;
+       }
+
+       pkg_formatted_field(file, pkg, "Package");
+       pkg_formatted_field(file, pkg, "Version");
+       pkg_formatted_field(file, pkg, "Depends");
+       pkg_formatted_field(file, pkg, "Recommends");
+       pkg_formatted_field(file, pkg, "Suggests");
+       pkg_formatted_field(file, pkg, "Provides");
+       pkg_formatted_field(file, pkg, "Replaces");
+       pkg_formatted_field(file, pkg, "Conflicts");
+       pkg_formatted_field(file, pkg, "Status");
+       pkg_formatted_field(file, pkg, "Essential");
+       pkg_formatted_field(file, pkg, "Architecture");
+       pkg_formatted_field(file, pkg, "Conffiles");
+       pkg_formatted_field(file, pkg, "Installed-Time");
+       pkg_formatted_field(file, pkg, "Auto-Installed");
+       fputs("\n", file);
 }
 
 /*
@@ -946,154 +984,173 @@ pkg_print_status(pkg_t * pkg, FILE * file)
                : isalpha((x)) ? (x) \
                : (x) + 256)
 
-static int
-verrevcmp(const char *val, const char *ref) {
-  if (!val) val= "";
-  if (!ref) ref= "";
-
-  while (*val || *ref) {
-    int first_diff= 0;
-
-    while ( (*val && !isdigit(*val)) || (*ref && !isdigit(*ref)) ) {
-      int vc= order(*val), rc= order(*ref);
-      if (vc != rc) return vc - rc;
-      val++; ref++;
-    }
-
-    while ( *val == '0' ) val++;
-    while ( *ref == '0' ) ref++;
-    while (isdigit(*val) && isdigit(*ref)) {
-      if (!first_diff) first_diff= *val - *ref;
-      val++; ref++;
-    }
-    if (isdigit(*val)) return 1;
-    if (isdigit(*ref)) return -1;
-    if (first_diff) return first_diff;
-  }
-  return 0;
+static int verrevcmp(const char *val, const char *ref)
+{
+       if (!val)
+               val = "";
+       if (!ref)
+               ref = "";
+
+       while (*val || *ref) {
+               int first_diff = 0;
+
+               while ((*val && !isdigit(*val)) || (*ref && !isdigit(*ref))) {
+                       int vc = order(*val), rc = order(*ref);
+                       if (vc != rc)
+                               return vc - rc;
+                       val++;
+                       ref++;
+               }
+
+               while (*val == '0')
+                       val++;
+               while (*ref == '0')
+                       ref++;
+               while (isdigit(*val) && isdigit(*ref)) {
+                       if (!first_diff)
+                               first_diff = *val - *ref;
+                       val++;
+                       ref++;
+               }
+               if (isdigit(*val))
+                       return 1;
+               if (isdigit(*ref))
+                       return -1;
+               if (first_diff)
+                       return first_diff;
+       }
+       return 0;
 }
 
-int
-pkg_compare_versions(const pkg_t *pkg, const pkg_t *ref_pkg)
+int pkg_compare_versions(const pkg_t * pkg, const pkg_t * ref_pkg)
 {
-     int r;
-
-     if (pkg->epoch > ref_pkg->epoch) {
-         return 1;
-     }
+       unsigned int epoch1 = (unsigned int) pkg_get_int(pkg, PKG_EPOCH);
+       unsigned int epoch2 = (unsigned int) pkg_get_int(ref_pkg, PKG_EPOCH);
+       char *revision1 = pkg_get_string(pkg, PKG_REVISION);
+       char *revision2 = pkg_get_string(ref_pkg, PKG_REVISION);
+       const char *version1 = pkg_get_string(pkg, PKG_VERSION);
+       const char *version2 = pkg_get_string(ref_pkg, PKG_VERSION);
+       int r;
+
+       if (epoch1 > epoch2) {
+               return 1;
+       }
 
-     if (pkg->epoch < ref_pkg->epoch) {
-         return -1;
-     }
+       if (epoch1 < epoch2) {
+               return -1;
+       }
 
-     r = verrevcmp(pkg->version, ref_pkg->version);
-     if (r) {
-         return r;
-     }
+       r = verrevcmp(version1, version2);
+       if (r) {
+               return r;
+       }
 
-     r = verrevcmp(pkg->revision, ref_pkg->revision);
-     if (r) {
-         return r;
-     }
+       r = verrevcmp(revision1, revision2);
+       if (r) {
+               return r;
+       }
 
-     return r;
+       return r;
 }
 
-
-int
-pkg_version_satisfied(pkg_t *it, pkg_t *ref, const char *op)
+int pkg_version_satisfied(pkg_t * it, pkg_t * ref, const char *op)
 {
-     int r;
+       int r;
 
-     r = pkg_compare_versions(it, ref);
+       r = pkg_compare_versions(it, ref);
 
-     if (strcmp(op, "<=") == 0 || strcmp(op, "<") == 0) {
-         return r <= 0;
-     }
+       if (strcmp(op, "<=") == 0 || strcmp(op, "<") == 0) {
+               return r <= 0;
+       }
 
-     if (strcmp(op, ">=") == 0 || strcmp(op, ">") == 0) {
-         return r >= 0;
-     }
+       if (strcmp(op, ">=") == 0 || strcmp(op, ">") == 0) {
+               return r >= 0;
+       }
 
-     if (strcmp(op, "<<") == 0) {
-         return r < 0;
-     }
+       if (strcmp(op, "<<") == 0) {
+               return r < 0;
+       }
 
-     if (strcmp(op, ">>") == 0) {
-         return r > 0;
-     }
+       if (strcmp(op, ">>") == 0) {
+               return r > 0;
+       }
 
-     if (strcmp(op, "=") == 0) {
-         return r == 0;
-     }
+       if (strcmp(op, "=") == 0) {
+               return r == 0;
+       }
 
-     fprintf(stderr, "unknown operator: %s", op);
-     return 0;
+       opkg_msg(ERROR, "Unknown operator: %s.\n", op);
+       return 0;
 }
 
-int
-pkg_name_version_and_architecture_compare(const void *p1, const void *p2)
+int pkg_name_version_and_architecture_compare(const void *p1, const void *p2)
 {
-     const pkg_t *a = *(const pkg_t**) p1;
-     const pkg_t *b = *(const pkg_t**) p2;
-     int namecmp;
-     int vercmp;
-     if (!a->name || !b->name) {
-       fprintf(stderr, "pkg_name_version_and_architecture_compare: a=%p a->name=%p b=%p b->name=%p\n",
-              a, a->name, b, b->name);
-       return 0;
-     }
-       
-     namecmp = strcmp(a->name, b->name);
-     if (namecmp)
-         return namecmp;
-     vercmp = pkg_compare_versions(a, b);
-     if (vercmp)
-         return vercmp;
-     if (!a->arch_priority || !b->arch_priority) {
-       fprintf(stderr, "pkg_name_version_and_architecture_compare: a=%p a->arch_priority=%i b=%p b->arch_priority=%i\n",
-              a, a->arch_priority, b, b->arch_priority);
-       return 0;
-     }
-     if (a->arch_priority > b->arch_priority)
-         return 1;
-     if (a->arch_priority < b->arch_priority)
-         return -1;
-     return 0;
+       const pkg_t * a = *(const pkg_t **)p1;
+       const pkg_t * b = *(const pkg_t **)p2;
+       int namecmp;
+       int vercmp;
+       int arch_prio1, arch_prio2;
+       if (!a->name || !b->name) {
+               opkg_msg(ERROR, "Internal error: a->name=%p, b->name=%p.\n",
+                        a->name, b->name);
+               return 0;
+       }
+
+       namecmp = strcmp(a->name, b->name);
+       if (namecmp)
+               return namecmp;
+       vercmp = pkg_compare_versions(a, b);
+       if (vercmp)
+               return vercmp;
+       arch_prio1 = pkg_get_arch_priority(a);
+       arch_prio2 = pkg_get_arch_priority(b);
+       if (!arch_prio1 || !arch_prio2) {
+               opkg_msg(ERROR,
+                        "Internal error: a->arch_priority=%i b->arch_priority=%i.\n",
+                        arch_prio1, arch_prio2);
+               return 0;
+       }
+       if (arch_prio1 > arch_prio2)
+               return 1;
+       if (arch_prio1 < arch_prio2)
+               return -1;
+       return 0;
 }
 
-int
-abstract_pkg_name_compare(const void *p1, const void *p2)
+int abstract_pkg_name_compare(const void *p1, const void *p2)
 {
-     const abstract_pkg_t *a = *(const abstract_pkg_t **)p1;
-     const abstract_pkg_t *b = *(const abstract_pkg_t **)p2;
-     if (!a->name || !b->name) {
-       fprintf(stderr, "abstract_pkg_name_compare: a=%p a->name=%p b=%p b->name=%p\n",
-              a, a->name, b, b->name);
-       return 0;
-     }
-     return strcmp(a->name, b->name);
+       const abstract_pkg_t *a = *(const abstract_pkg_t **)p1;
+       const abstract_pkg_t *b = *(const abstract_pkg_t **)p2;
+       if (!a->name || !b->name) {
+               opkg_msg(ERROR, "Internal error: a->name=%p b->name=%p.\n",
+                        a->name, b->name);
+               return 0;
+       }
+       return strcmp(a->name, b->name);
 }
 
-
-char *
-pkg_version_str_alloc(pkg_t *pkg)
+char *pkg_version_str_alloc(pkg_t * pkg)
 {
-       char *version;
+       const char *verstr;
+       char *version, *revptr;
+       unsigned int epoch = (unsigned int) pkg_get_int(pkg, PKG_EPOCH);
+
+       revptr = pkg_get_string(pkg, PKG_REVISION);
+       verstr = pkg_get_string(pkg, PKG_VERSION);
 
-       if (pkg->epoch) {
-               if (pkg->revision)
+       if (epoch) {
+               if (revptr)
                        sprintf_alloc(&version, "%d:%s-%s",
-                               pkg->epoch, pkg->version, pkg->revision);
+                                     epoch, verstr, revptr);
                else
                        sprintf_alloc(&version, "%d:%s",
-                               pkg->epoch, pkg->version);
+                                     epoch, verstr);
        } else {
-               if (pkg->revision)
+               if (revptr)
                        sprintf_alloc(&version, "%s-%s",
-                               pkg->version, pkg->revision);
+                                     verstr, revptr);
                else
-                       version = xstrdup(pkg->version);
+                       version = xstrdup(verstr);
        }
 
        return version;
@@ -1102,121 +1159,132 @@ pkg_version_str_alloc(pkg_t *pkg)
 /*
  * XXX: this should be broken into two functions
  */
-str_list_t *
-pkg_get_installed_files(opkg_conf_t *conf, pkg_t *pkg)
+str_list_t *pkg_get_installed_files(pkg_t * pkg)
 {
-     int err, fd;
-     char *list_file_name = NULL;
-     FILE *list_file = NULL;
-     char *line;
-     char *installed_file_name;
-     int rootdirlen = 0;
-
-     pkg->installed_files_ref_cnt++;
-
-     if (pkg->installed_files) {
-         return pkg->installed_files;
-     }
-
-     pkg->installed_files = str_list_alloc();
-
-     /* For uninstalled packages, get the file list directly from the package.
-       For installed packages, look at the package.list file in the database.
-     */
-     if (pkg->state_status == SS_NOT_INSTALLED || pkg->dest == NULL) {
-         if (pkg->local_filename == NULL) {
-              return pkg->installed_files;
-         }
-         /* XXX: CLEANUP: Maybe rewrite this to avoid using a temporary
-            file. In other words, change deb_extract so that it can
-            simply return the file list as a char *[] rather than
-            insisting on writing in to a FILE * as it does now. */
-         sprintf_alloc(&list_file_name, "%s/%s.list.XXXXXX",
-                                         conf->tmp_dir, pkg->name);
-         fd = mkstemp(list_file_name);
-         if (fd == -1) {
-              opkg_message(conf, OPKG_ERROR, "%s: mkstemp(%s): %s",
-                              __FUNCTION__, list_file_name, strerror(errno));
-              free(list_file_name);
-              return pkg->installed_files;
-         }
-         list_file = fdopen(fd, "r+");
-         if (list_file == NULL) {
-              opkg_message(conf, OPKG_ERROR, "%s: fdopen: %s",
-                              __FUNCTION__, strerror(errno));
-              close(fd);
-              unlink(list_file_name);
-              free(list_file_name);
-              return pkg->installed_files;
-         }
-         err = pkg_extract_data_file_names_to_stream(pkg, list_file);
-         if (err) {
-              opkg_message(conf, OPKG_ERROR, "%s: Error extracting file list "
-                              "from %s: %s\n", __FUNCTION__,
-                              pkg->local_filename, strerror(err));
-              fclose(list_file);
-              unlink(list_file_name);
-              free(list_file_name);
-              return pkg->installed_files;
-         }
-         rewind(list_file);
-     } else {
-         sprintf_alloc(&list_file_name, "%s/%s.list",
-                       pkg->dest->info_dir, pkg->name);
-         list_file = fopen(list_file_name, "r");
-         if (list_file == NULL) {
-              opkg_message(conf, OPKG_ERROR, "%s: fopen(%s): %s\n",
-                      __FUNCTION__, list_file_name, strerror(errno));
-              free(list_file_name);
-              return pkg->installed_files;
-         }
-         free(list_file_name);
-     }
-
-     if (conf->offline_root)
-          rootdirlen = strlen(conf->offline_root);
-
-     while (1) {
-         char *file_name;
-       
-         line = file_read_line_alloc(list_file);
-         if (line == NULL) {
-              break;
-         }
-         file_name = line;
-
-         if (pkg->state_status == SS_NOT_INSTALLED || pkg->dest == NULL) {
-              if (*file_name == '.') {
-                   file_name++;
-              }
-              if (*file_name == '/') {
-                   file_name++;
-              }
-              sprintf_alloc(&installed_file_name, "%s%s",
-                              pkg->dest->root_dir, file_name);
-         } else {
-              if (conf->offline_root &&
-                      strncmp(conf->offline_root, file_name, rootdirlen)) {
-                   sprintf_alloc(&installed_file_name, "%s%s",
-                                   conf->offline_root, file_name);
-              } else {
-                   // already contains root_dir as header -> ABSOLUTE
-                   sprintf_alloc(&installed_file_name, "%s", file_name);
-              }
-         }
-         str_list_append(pkg->installed_files, installed_file_name);
-          free(installed_file_name);
-         free(line);
-     }
-
-     fclose(list_file);
-
-     if (pkg->state_status == SS_NOT_INSTALLED || pkg->dest == NULL) {
-         unlink(list_file_name);
-         free(list_file_name);
-     }
-
-     return pkg->installed_files;
+       int err, fd;
+       char *list_file_name = NULL;
+       FILE *list_file = NULL;
+       char *line;
+       char *installed_file_name;
+       unsigned int rootdirlen = 0;
+       int list_from_package;
+       const char *local_filename;
+
+       pkg->installed_files_ref_cnt++;
+
+       if (pkg->installed_files) {
+               return pkg->installed_files;
+       }
+
+       pkg->installed_files = str_list_alloc();
+
+       /*
+        * For installed packages, look at the package.list file in the database.
+        * For uninstalled packages, get the file list directly from the package.
+        */
+       if (pkg->state_status == SS_NOT_INSTALLED || pkg->dest == NULL)
+               list_from_package = 1;
+       else
+               list_from_package = 0;
+
+       if (list_from_package) {
+               local_filename = pkg_get_string(pkg, PKG_LOCAL_FILENAME);
+
+               if (!local_filename) {
+                       return pkg->installed_files;
+               }
+               /* XXX: CLEANUP: Maybe rewrite this to avoid using a temporary
+                  file. In other words, change deb_extract so that it can
+                  simply return the file list as a char *[] rather than
+                  insisting on writing it to a FILE * as it does now. */
+               sprintf_alloc(&list_file_name, "%s/%s.list.XXXXXX",
+                             conf->tmp_dir, pkg->name);
+               fd = mkstemp(list_file_name);
+               if (fd == -1) {
+                       opkg_perror(ERROR, "Failed to make temp file %s.",
+                                   list_file_name);
+                       free(list_file_name);
+                       return pkg->installed_files;
+               }
+               list_file = fdopen(fd, "r+");
+               if (list_file == NULL) {
+                       opkg_perror(ERROR, "Failed to fdopen temp file %s.",
+                                   list_file_name);
+                       close(fd);
+                       unlink(list_file_name);
+                       free(list_file_name);
+                       return pkg->installed_files;
+               }
+               err = pkg_extract_data_file_names_to_stream(pkg, list_file);
+               if (err) {
+                       opkg_msg(ERROR, "Error extracting file list from %s.\n",
+                                local_filename);
+                       fclose(list_file);
+                       unlink(list_file_name);
+                       free(list_file_name);
+                       str_list_deinit(pkg->installed_files);
+                       pkg->installed_files = NULL;
+                       return NULL;
+               }
+               rewind(list_file);
+       } else {
+               sprintf_alloc(&list_file_name, "%s/%s.list",
+                             pkg->dest->info_dir, pkg->name);
+               list_file = fopen(list_file_name, "r");
+               if (list_file == NULL) {
+                       opkg_perror(ERROR, "Failed to open %s", list_file_name);
+                       free(list_file_name);
+                       return pkg->installed_files;
+               }
+               free(list_file_name);
+       }
+
+       if (conf->offline_root)
+               rootdirlen = strlen(conf->offline_root);
+
+       while (1) {
+               char *file_name;
+
+               line = file_read_line_alloc(list_file);
+               if (line == NULL) {
+                       break;
+               }
+               file_name = line;
+
+               if (list_from_package) {
+                       if (*file_name == '.') {
+                               file_name++;
+                       }
+                       if (*file_name == '/') {
+                               file_name++;
+                       }
+                       sprintf_alloc(&installed_file_name, "%s%s",
+                                     pkg->dest->root_dir, file_name);
+               } else {
+                       if (conf->offline_root &&
+                           strncmp(conf->offline_root, file_name,
+                                   rootdirlen)) {
+                               sprintf_alloc(&installed_file_name, "%s%s",
+                                             conf->offline_root, file_name);
+                       } else {
+                               // already contains root_dir as header -> ABSOLUTE
+                               sprintf_alloc(&installed_file_name, "%s",
+                                             file_name);
+                       }
+               }
+               str_list_append(pkg->installed_files, installed_file_name);
+               free(installed_file_name);
+               free(line);
+       }
+
+       fclose(list_file);
+
+       if (list_from_package) {
+               unlink(list_file_name);
+               free(list_file_name);
+       }
+
+       return pkg->installed_files;
 }
 
 /* XXX: CLEANUP: This function and it's counterpart,
@@ -1224,28 +1292,26 @@ pkg_get_installed_files(opkg_conf_t *conf, pkg_t *pkg)
    convention. Nor the alloc/free convention. But, then again, neither
    of these conventions currrently fit the way these two functions
    work. */
-void
-pkg_free_installed_files(pkg_t *pkg)
+void pkg_free_installed_files(pkg_t * pkg)
 {
-     pkg->installed_files_ref_cnt--;
+       pkg->installed_files_ref_cnt--;
 
-     if (pkg->installed_files_ref_cnt > 0)
-         return;
+       if (pkg->installed_files_ref_cnt > 0)
+               return;
 
-     if (pkg->installed_files) {
-         str_list_purge(pkg->installed_files);
-     }
+       if (pkg->installed_files) {
+               str_list_purge(pkg->installed_files);
+       }
 
-     pkg->installed_files = NULL;
+       pkg->installed_files = NULL;
 }
 
-void
-pkg_remove_installed_files_list(opkg_conf_t *conf, pkg_t *pkg)
+void pkg_remove_installed_files_list(pkg_t * pkg)
 {
        char *list_file_name;
 
        sprintf_alloc(&list_file_name, "%s/%s.list",
-               pkg->dest->info_dir, pkg->name);
+                     pkg->dest->info_dir, pkg->name);
 
        if (!conf->noaction)
                (void)unlink(list_file_name);
@@ -1253,242 +1319,190 @@ pkg_remove_installed_files_list(opkg_conf_t *conf, pkg_t *pkg)
        free(list_file_name);
 }
 
-conffile_t *
-pkg_get_conffile(pkg_t *pkg, const char *file_name)
+conffile_t *pkg_get_conffile(pkg_t * pkg, const char *file_name)
 {
-     conffile_list_elt_t *iter;
-     conffile_t *conffile;
+       conffile_list_elt_t *iter;
+       conffile_list_t *cl;
+       conffile_t *conffile;
 
-     if (pkg == NULL) {
-         return NULL;
-     }
+       if (pkg == NULL) {
+               return NULL;
+       }
 
-     for (iter = nv_pair_list_first(&pkg->conffiles); iter; iter = nv_pair_list_next(&pkg->conffiles, iter)) {
-         conffile = (conffile_t *)iter->data;
+       cl = pkg_get_ptr(pkg, PKG_CONFFILES);
 
-         if (strcmp(conffile->name, file_name) == 0) {
-              return conffile;
-         }
-     }
+       for (iter = cl ? nv_pair_list_first(cl) : NULL; iter;
+            iter = nv_pair_list_next(cl, iter)) {
+               conffile = (conffile_t *) iter->data;
 
-     return NULL;
-}
+               if (strcmp(conffile->name, file_name) == 0) {
+                       return conffile;
+               }
+       }
 
-int
-pkg_run_script(opkg_conf_t *conf, pkg_t *pkg,
-               const char *script, const char *args)
-{
-     int err;
-     char *path;
-     char *cmd;
-
-     if (conf->noaction)
-            return 0;
-
-     /* XXX: CLEANUP: There must be a better way to handle maintainer
-       scripts when running with offline_root mode and/or a dest other
-       than '/'. I've been playing around with some clever chroot
-       tricks and I might come up with something workable. */
-     /*
-      * Attempt to provide a restricted environment for offline operation
-      * Need the following set as a minimum:
-      * OPKG_OFFLINE_ROOT = absolute path to root dir
-      * D                 = absolute path to root dir (for OE generated postinst)
-      * PATH              = something safe (a restricted set of utilities)
-      */
-
-     if (conf->offline_root) {
-          if (conf->offline_root_path) {
-            setenv("PATH", conf->offline_root_path, 1);
-          } else {
-            opkg_message(conf, OPKG_NOTICE, 
-               "(offline root mode: not running %s.%s)\n", pkg->name, script);
-           return 0;
-          }
-         setenv("OPKG_OFFLINE_ROOT", conf->offline_root, 1);
-         setenv("D", conf->offline_root, 1);
-     }
-
-     /* XXX: FEATURE: When conf->offline_root is set, we should run the
-       maintainer script within a chroot environment. */
-
-     /* Installed packages have scripts in pkg->dest->info_dir, uninstalled packages
-       have scripts in pkg->tmp_unpack_dir. */
-     if (pkg->state_status == SS_INSTALLED || pkg->state_status == SS_UNPACKED) {
-         if (pkg->dest == NULL) {
-              fprintf(stderr, "%s: ERROR: installed package %s has a NULL dest\n",
-                      __FUNCTION__, pkg->name);
-              return EINVAL;
-         }
-         sprintf_alloc(&path, "%s/%s.%s", pkg->dest->info_dir, pkg->name, script);
-     } else {
-         if (pkg->tmp_unpack_dir == NULL) {
-              fprintf(stderr, "%s: ERROR: uninstalled package %s has a NULL tmp_unpack_dir\n",
-                      __FUNCTION__, pkg->name);
-              return EINVAL;
-         }
-         sprintf_alloc(&path, "%s/%s", pkg->tmp_unpack_dir, script);
-     }
-
-     opkg_message(conf, OPKG_INFO, "Running script %s\n", path);
-
-     setenv("PKG_ROOT",
-           pkg->dest ? pkg->dest->root_dir : conf->default_dest->root_dir, 1);
-
-     if (! file_exists(path)) {
-         free(path);
-         return 0;
-     }
-
-     sprintf_alloc(&cmd, "%s %s", path, args);
-     free(path);
-     {
-         const char *argv[] = {"sh", "-c", cmd, NULL};
-         err = xsystem(argv);
-     }
-     free(cmd);
-
-     if (err) {
-         fprintf(stderr, "%s script returned status %d\n", script, err);
-         return err;
-     }
-
-     return 0;
+       return NULL;
 }
 
-int
-pkg_arch_supported(opkg_conf_t *conf, pkg_t *pkg)
+int pkg_run_script(pkg_t * pkg, const char *script, const char *args)
 {
-     nv_pair_list_elt_t *l;
+       int err;
+       char *path;
+       char *cmd;
+       char *tmp_unpack_dir;
+
+       if (conf->noaction)
+               return 0;
+
+       /* XXX: FEATURE: When conf->offline_root is set, we should run the
+          maintainer script within a chroot environment. */
+       if (conf->offline_root && !conf->force_postinstall) {
+               opkg_msg(INFO, "Offline root mode: not running %s.%s.\n",
+                        pkg->name, script);
+               return 0;
+       }
+
+       /* Installed packages have scripts in pkg->dest->info_dir, uninstalled packages
+          have scripts in tmp_unpack_dir. */
+       if (pkg->state_status == SS_INSTALLED
+           || pkg->state_status == SS_UNPACKED) {
+               if (pkg->dest == NULL) {
+                       opkg_msg(ERROR, "Internal error: %s has a NULL dest.\n",
+                                pkg->name);
+                       return -1;
+               }
+               sprintf_alloc(&path, "%s/%s.%s", pkg->dest->info_dir, pkg->name,
+                             script);
+       } else {
+               tmp_unpack_dir = pkg_get_string(pkg, PKG_TMP_UNPACK_DIR);
+               if (tmp_unpack_dir == NULL) {
+                       opkg_msg(ERROR,
+                                "Internal error: %s has a NULL tmp_unpack_dir.\n",
+                                pkg->name);
+                       return -1;
+               }
+               sprintf_alloc(&path, "%s/%s", tmp_unpack_dir, script);
+       }
+
+       opkg_msg(INFO, "Running script %s.\n", path);
+
+       setenv("PKG_ROOT",
+              pkg->dest ? pkg->dest->root_dir : conf->default_dest->root_dir,
+              1);
 
-     if (!pkg->architecture)
-         return 1;
+       if (pkg->is_upgrade)
+               setenv("PKG_UPGRADE", "1", 1);
+       else
+               setenv("PKG_UPGRADE", "0", 1);
 
-     list_for_each_entry(l , &conf->arch_list.head, node) {
-         nv_pair_t *nv = (nv_pair_t *)l->data;
-         if (strcmp(nv->name, pkg->architecture) == 0) {
-              opkg_message(conf, OPKG_DEBUG, "arch %s (priority %s) supported for pkg %s\n", nv->name, nv->value, pkg->name);
-              return 1;
-         }
-     }
+       if (!file_exists(path)) {
+               free(path);
+               return 0;
+       }
+
+       sprintf_alloc(&cmd, "%s %s", path, args);
+       free(path);
+       {
+               const char *argv[] = { "sh", "-c", cmd, NULL };
+               err = xsystem(argv);
+       }
+       free(cmd);
 
-     opkg_message(conf, OPKG_DEBUG, "arch %s unsupported for pkg %s\n", pkg->architecture, pkg->name);
-     return 0;
+       if (err) {
+               opkg_msg(ERROR,
+                        "package \"%s\" %s script returned status %d.\n",
+                        pkg->name, script, err);
+               return err;
+       }
+
+       return 0;
 }
 
-int
-pkg_get_arch_priority(opkg_conf_t *conf, const char *archname)
+int pkg_arch_supported(pkg_t * pkg)
 {
-     nv_pair_list_elt_t *l;
-
-     list_for_each_entry(l , &conf->arch_list.head, node) {
-         nv_pair_t *nv = (nv_pair_t *)l->data;
-         if (strcmp(nv->name, archname) == 0) {
-              int priority = strtol(nv->value, NULL, 0);
-              return priority;
-         }
-     }
-     return 0;
+       nv_pair_list_elt_t *l;
+       char *architecture = pkg_get_architecture(pkg);
+
+       if (!architecture)
+               return 1;
+
+       list_for_each_entry(l, &conf->arch_list.head, node) {
+               nv_pair_t *nv = (nv_pair_t *) l->data;
+               if (strcmp(nv->name, architecture) == 0) {
+                       opkg_msg(DEBUG,
+                                "Arch %s (priority %s) supported for pkg %s.\n",
+                                nv->name, nv->value, pkg->name);
+                       return 1;
+               }
+       }
+
+       opkg_msg(DEBUG, "Arch %s unsupported for pkg %s.\n",
+                architecture, pkg->name);
+       return 0;
 }
 
-void
-pkg_info_preinstall_check(opkg_conf_t *conf)
+void pkg_info_preinstall_check(void)
 {
-     int i;
-     hash_table_t *pkg_hash = &conf->pkg_hash;
-     pkg_vec_t *available_pkgs = pkg_vec_alloc();
-     pkg_vec_t *installed_pkgs = pkg_vec_alloc();
-
-     opkg_message(conf, OPKG_INFO, "pkg_info_preinstall_check: updating arch priority for each package\n");
-     pkg_hash_fetch_available(pkg_hash, available_pkgs);
-     /* update arch_priority for each package */
-     for (i = 0; i < available_pkgs->len; i++) {
-         pkg_t *pkg = available_pkgs->pkgs[i];
-         int arch_priority = 1;
-         if (!pkg)
-              continue;
-         // opkg_message(conf, OPKG_DEBUG2, " package %s version=%s arch=%p:", pkg->name, pkg->version, pkg->architecture);
-         if (pkg->architecture) 
-              arch_priority = pkg_get_arch_priority(conf, pkg->architecture);
-         else 
-              opkg_message(conf, OPKG_ERROR, "pkg_info_preinstall_check: no architecture for package %s\n", pkg->name);
-         // opkg_message(conf, OPKG_DEBUG2, "%s arch_priority=%d\n", pkg->architecture, arch_priority);
-         pkg->arch_priority = arch_priority;
-     }
-
-     for (i = 0; i < available_pkgs->len; i++) {
-         pkg_t *pkg = available_pkgs->pkgs[i];
-         if (!pkg->arch_priority && (pkg->state_flag || (pkg->state_want != SW_UNKNOWN))) {
-              /* clear flags and want for any uninstallable package */
-              opkg_message(conf, OPKG_DEBUG, "Clearing state_want and state_flag for pkg=%s (arch_priority=%d flag=%d want=%d)\n", 
-                           pkg->name, pkg->arch_priority, pkg->state_flag, pkg->state_want);
-              pkg->state_want = SW_UNKNOWN;
-              pkg->state_flag = 0;
-         }
-     }
-     pkg_vec_free(available_pkgs);
-
-     /* update the file owner data structure */
-     opkg_message(conf, OPKG_INFO, "pkg_info_preinstall_check: update file owner list\n");
-     pkg_hash_fetch_all_installed(pkg_hash, installed_pkgs);
-     for (i = 0; i < installed_pkgs->len; i++) {
-         pkg_t *pkg = installed_pkgs->pkgs[i];
-         str_list_t *installed_files = pkg_get_installed_files(conf, pkg); /* this causes installed_files to be cached */
-         str_list_elt_t *iter, *niter;
-         if (installed_files == NULL) {
-              opkg_message(conf, OPKG_ERROR, "No installed files for pkg %s\n", pkg->name);
-              break;
-         }
-         for (iter = str_list_first(installed_files), niter = str_list_next(installed_files, iter); 
-                  iter; 
-                  iter = niter, niter = str_list_next(installed_files, iter)) {
-              char *installed_file = (char *) iter->data;
-              // opkg_message(conf, OPKG_DEBUG2, "pkg %s: file=%s\n", pkg->name, installed_file);
-              file_hash_set_file_owner(conf, installed_file, pkg);
-         }
-         pkg_free_installed_files(pkg);
-     }
-     pkg_vec_free(installed_pkgs);
+       int i;
+       pkg_vec_t *installed_pkgs = pkg_vec_alloc();
+
+       /* update the file owner data structure */
+       opkg_msg(INFO, "Updating file owner list.\n");
+       pkg_hash_fetch_all_installed(installed_pkgs);
+       for (i = 0; i < installed_pkgs->len; i++) {
+               pkg_t *pkg = installed_pkgs->pkgs[i];
+               str_list_t *installed_files = pkg_get_installed_files(pkg);     /* this causes installed_files to be cached */
+               str_list_elt_t *iter, *niter;
+               if (installed_files == NULL) {
+                       opkg_msg(ERROR, "Failed to determine installed "
+                                "files for pkg %s.\n", pkg->name);
+                       break;
+               }
+               for (iter = str_list_first(installed_files), niter =
+                    str_list_next(installed_files, iter); iter;
+                    iter = niter, niter =
+                    str_list_next(installed_files, iter)) {
+                       char *installed_file = (char *)iter->data;
+                       file_hash_set_file_owner(installed_file, pkg);
+               }
+               pkg_free_installed_files(pkg);
+       }
+       pkg_vec_free(installed_pkgs);
 }
 
 struct pkg_write_filelist_data {
-     opkg_conf_t *conf;
-     pkg_t *pkg;
-     FILE *stream;
+       pkg_t *pkg;
+       FILE *stream;
 };
 
-void
+static void
 pkg_write_filelist_helper(const char *key, void *entry_, void *data_)
 {
-     struct pkg_write_filelist_data *data = data_;
-     pkg_t *entry = entry_;
-     if (entry == data->pkg) {
-         fprintf(data->stream, "%s\n", key);
-     }
+       struct pkg_write_filelist_data *data = data_;
+       pkg_t *entry = entry_;
+       if (entry == data->pkg) {
+               fprintf(data->stream, "%s\n", key);
+       }
 }
 
-int
-pkg_write_filelist(opkg_conf_t *conf, pkg_t *pkg)
+int pkg_write_filelist(pkg_t * pkg)
 {
        struct pkg_write_filelist_data data;
        char *list_file_name;
 
        sprintf_alloc(&list_file_name, "%s/%s.list",
-                       pkg->dest->info_dir, pkg->name);
+                     pkg->dest->info_dir, pkg->name);
 
-       opkg_message(conf, OPKG_INFO, "%s: creating %s file for pkg %s\n",
-                       __FUNCTION__, list_file_name, pkg->name);
+       opkg_msg(INFO, "Creating %s file for pkg %s.\n",
+                list_file_name, pkg->name);
 
        data.stream = fopen(list_file_name, "w");
        if (!data.stream) {
-               opkg_message(conf, OPKG_ERROR, "%s: fopen(%s, \"w\"): %s\n",
-                       __FUNCTION__, list_file_name, strerror(errno));
+               opkg_perror(ERROR, "Failed to open %s", list_file_name);
                free(list_file_name);
                return -1;
        }
 
        data.pkg = pkg;
-       data.conf = conf;
        hash_table_foreach(&conf->file_hash, pkg_write_filelist_helper, &data);
        fclose(data.stream);
        free(list_file_name);
@@ -1498,30 +1512,27 @@ pkg_write_filelist(opkg_conf_t *conf, pkg_t *pkg)
        return 0;
 }
 
-int
-pkg_write_changed_filelists(opkg_conf_t *conf)
+int pkg_write_changed_filelists(void)
 {
        pkg_vec_t *installed_pkgs = pkg_vec_alloc();
-       hash_table_t *pkg_hash = &conf->pkg_hash;
        int i, err, ret = 0;
 
        if (conf->noaction)
                return 0;
 
-       opkg_message(conf, OPKG_INFO, "%s: saving changed filelists\n",
-                       __FUNCTION__);
+       opkg_msg(INFO, "Saving changed filelists.\n");
 
-       pkg_hash_fetch_all_installed(pkg_hash, installed_pkgs);
+       pkg_hash_fetch_all_installed(installed_pkgs);
        for (i = 0; i < installed_pkgs->len; i++) {
                pkg_t *pkg = installed_pkgs->pkgs[i];
                if (pkg->state_flag & SF_FILELIST_CHANGED) {
-                       err = pkg_write_filelist(conf, pkg);
+                       err = pkg_write_filelist(pkg);
                        if (err)
                                ret = -1;
                }
        }
 
-       pkg_vec_free (installed_pkgs);
+       pkg_vec_free(installed_pkgs);
 
        return ret;
 }