pkg: alternatives support
authorYousong Zhou <yszhou4tech@gmail.com>
Sat, 18 Mar 2017 08:45:53 +0000 (16:45 +0800)
committerYousong Zhou <yszhou4tech@gmail.com>
Tue, 2 May 2017 14:28:33 +0000 (22:28 +0800)
It's a list of specs of the following form seprated by commas to describe
alternatives provided by the package

    <prio>:<path>:<altpath>

where <path> will be a symbolic link to <altpath> of the highest <prio>

Size comparison on x86_64 after the change

    function                                             old     new   delta
    pkg_alternatives_update                                -     587    +587
    pkg_parse_line                                      2101    2609    +522
    .rodata                                            24594   24738    +144
    pkg_formatted_field                                 2385    2528    +143
    pkg_deinit                                           427     486     +59
    pkg_print_status                                     264     280     +16
    opkg_configure                                        59      69     +10
    xreadlink                                            120     128      +8
    opkg_remove_pkg                                     1079    1087      +8
    ------------------------------------------------------------------------------
    (add/remove: 2/0 grow/shrink: 8/0 up/down: 1483/0)           Total: 1497 bytes

Signed-off-by: Yousong Zhou <yszhou4tech@gmail.com>
libopkg/CMakeLists.txt
libopkg/opkg_configure.c
libopkg/opkg_remove.c
libopkg/pkg.c
libopkg/pkg.h
libopkg/pkg_alternatives.c [new file with mode: 0644]
libopkg/pkg_alternatives.h [new file with mode: 0644]
libopkg/pkg_parse.c
libopkg/pkg_parse.h

index 637dadb..1b2a949 100644 (file)
@@ -9,7 +9,7 @@ ADD_LIBRARY(opkg STATIC
        active_list.c conffile.c conffile_list.c file_util.c hash_table.c
        nv_pair.c nv_pair_list.c opkg.c opkg_cmd.c opkg_conf.c opkg_configure.c
        opkg_download.c opkg_install.c opkg_message.c opkg_remove.c
-       opkg_upgrade.c opkg_utils.c parse_util.c pkg.c pkg_depends.c pkg_dest.c
+       opkg_upgrade.c opkg_utils.c parse_util.c pkg.c pkg_alternatives.c pkg_depends.c pkg_dest.c
        pkg_dest_list.c pkg_extract.c pkg_hash.c pkg_parse.c pkg_src.c
        pkg_src_list.c pkg_vec.c sha256.c sprintf_alloc.c str_list.c
        void_list.c xregex.c xsystem.c
index dc05f1e..a043c52 100644 (file)
@@ -21,6 +21,7 @@
 #include "opkg_configure.h"
 #include "opkg_message.h"
 #include "opkg_cmd.h"
+#include "pkg_alternatives.h"
 
 int opkg_configure(pkg_t * pkg)
 {
@@ -38,5 +39,7 @@ int opkg_configure(pkg_t * pkg)
                return err;
        }
 
+       pkg_alternatives_update(pkg);
+
        return 0;
 }
index 694c3f3..96ca558 100644 (file)
@@ -22,6 +22,7 @@
 #include "opkg_message.h"
 #include "opkg_remove.h"
 #include "opkg_cmd.h"
+#include "pkg_alternatives.h"
 #include "file_util.h"
 #include "sprintf_alloc.h"
 #include "libbb/libbb.h"
@@ -312,6 +313,7 @@ int opkg_remove_pkg(pkg_t * pkg, int from_upgrade)
 
        remove_maintainer_scripts(pkg);
        pkg->state_status = SS_NOT_INSTALLED;
+       pkg_alternatives_update(pkg);
 
        if (parent_pkg)
                parent_pkg->state_status = SS_NOT_INSTALLED;
index 551c629..21f239e 100644 (file)
@@ -319,6 +319,20 @@ void pkg_deinit(pkg_t * pkg)
                                free(ptr);
                        }
 
+                       pkg_set_ptr(pkg, blob_id(cur), NULL);
+                       break;
+               case PKG_ALTERNATIVES:
+                       ptr = pkg_get_ptr(pkg, blob_id(cur));
+
+                       if (ptr) {
+                               struct pkg_alternatives *pkg_alts = ptr;
+
+                               while (pkg_alts->nalts)
+                                       free(pkg_alts->alts[--pkg_alts->nalts]);
+                               free(pkg_alts->alts);
+                               free(pkg_alts);
+                       }
+
                        pkg_set_ptr(pkg, blob_id(cur), NULL);
                        break;
                }
@@ -636,7 +650,22 @@ void pkg_formatted_field(FILE * fp, pkg_t * pkg, const char *field)
        switch (field[0]) {
        case 'a':
        case 'A':
-               if (strcasecmp(field, "Architecture") == 0) {
+               if (strcasecmp(field, "Alternatives") == 0) {
+                       struct pkg_alternatives *pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+
+                       if (pkg_alts && pkg_alts->nalts > 0) {
+                               int i;
+                               struct pkg_alternative *alt;
+
+                               alt = pkg_alts->alts[0];
+                               fprintf(fp, "Alternatives: %d:%s:%s", alt->prio, alt->path, alt->altpath);
+                               for (i = 1; i < pkg_alts->nalts; i++) {
+                                       alt = pkg_alts->alts[i];
+                                       fprintf(fp, ", %d:%s:%s", alt->prio, alt->path, alt->altpath);
+                               }
+                               fputs("\n", fp);
+                       }
+               } else if (strcasecmp(field, "Architecture") == 0) {
                        p = pkg_get_architecture(pkg);
                        if (p) {
                                fprintf(fp, "Architecture: %s\n",
@@ -938,6 +967,7 @@ void pkg_print_status(pkg_t * pkg, FILE * file)
        pkg_formatted_field(file, pkg, "Conffiles");
        pkg_formatted_field(file, pkg, "Installed-Time");
        pkg_formatted_field(file, pkg, "Auto-Installed");
+       pkg_formatted_field(file, pkg, "Alternatives");
        fputs("\n", file);
 }
 
index cf405b1..600fc9e 100644 (file)
@@ -100,6 +100,7 @@ enum pkg_fields {
        PKG_DEPENDS,
        PKG_CONFLICTS,
        PKG_CONFFILES,
+       PKG_ALTERNATIVES,
 };
 
 struct abstract_pkg {
@@ -118,6 +119,24 @@ struct abstract_pkg {
 
 #include "pkg_depends.h"
 
+enum pkg_alternative_field {
+       PAF_PRIO,
+       PAF_PATH,
+       PAF_ALTPATH,
+       __PAF_MAX,
+};
+
+struct pkg_alternative {
+       int prio;
+       char *path;
+       char *altpath;
+};
+
+struct pkg_alternatives {
+       int nalts;
+       struct pkg_alternative **alts;
+};
+
 /* XXX: CLEANUP: I'd like to clean up pkg_t in several ways:
 
    The 3 version fields should go into a single version struct. (This
diff --git a/libopkg/pkg_alternatives.c b/libopkg/pkg_alternatives.c
new file mode 100644 (file)
index 0000000..66b64de
--- /dev/null
@@ -0,0 +1,113 @@
+/* pkg_alternatives.c - the opkg package management system
+
+   Copyright (C) 2017 Yousong Zhou
+
+   This program 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 2, or (at
+   your option) any later version.
+
+   This program 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.
+*/
+
+#include <stdio.h>
+#include <sys/types.h>         /* stat */
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "libbb/libbb.h"
+#include "opkg_message.h"
+#include "pkg.h"
+#include "pkg_hash.h"
+#include "pkg_alternatives.h"
+#include "sprintf_alloc.h"
+
+static int pkg_alternatives_update_path(pkg_t *pkg, const pkg_vec_t *installed, const char *path)
+{
+       struct pkg_alternatives *pkg_alts;
+       struct pkg_alternative *the_alt = NULL;
+       pkg_t *the_pkg = pkg;
+       int i, j;
+       int r;
+       char *path_in_dest;
+
+       for (i = 0; i < installed->len; i++) {
+               pkg_t *pkg = installed->pkgs[i];
+               pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+               if (!pkg_alts)
+                       continue;
+
+               for (j = 0; j < pkg_alts->nalts; j++) {
+                       struct pkg_alternative *alt = pkg_alts->alts[j];
+
+                       if (strcmp(path, alt->path))
+                               continue;
+                       if (!the_alt || the_alt->prio < alt->prio) {
+                               the_pkg = pkg;
+                               the_alt = alt;
+                       }
+               }
+       }
+
+       /* path is assumed to be an absolute one */
+       sprintf_alloc(&path_in_dest, "%s%s", the_pkg->dest->root_dir, &path[1]);
+       if (!path_in_dest)
+               return -1;
+
+       if (the_alt) {
+               struct stat sb;
+
+               r = lstat(path_in_dest, &sb);
+               if (!r) {
+                       char *realpath;
+
+                       if (!S_ISLNK(sb.st_mode)) {
+                               opkg_msg(ERROR, "%s exists but is not a symlink\n", path_in_dest);
+                               r = -1;
+                               goto out;
+                       }
+                       realpath = xreadlink(path_in_dest);
+                       if (realpath && strcmp(realpath, the_alt->altpath))
+                               unlink(path_in_dest);
+                       free(realpath);
+               } else if (errno != ENOENT) {
+                       goto out;
+               }
+               r = symlink(the_alt->altpath, path_in_dest);
+               if (r)
+                       opkg_msg(INFO, "failed symlinking %s -> %s\n", path_in_dest, the_alt->altpath);
+       } else {
+               unlink(path_in_dest);
+               r = 0;
+       }
+
+out:
+       free(path_in_dest);
+       return r;
+}
+
+int pkg_alternatives_update(pkg_t * pkg)
+{
+       int r = 0;
+       int i;
+       struct pkg_alternatives *pkg_alts;
+       struct pkg_alternative *alt = NULL;
+       pkg_vec_t *installed;
+
+       pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+       if (!pkg_alts)
+               return 0;
+
+       installed = pkg_vec_alloc();
+       pkg_hash_fetch_all_installed(installed);
+       for (i = 0; i < pkg_alts->nalts; i++) {
+               alt = pkg_alts->alts[i];
+               r |= pkg_alternatives_update_path(pkg, installed, alt->path);
+       }
+       pkg_vec_free(installed);
+
+       return r;
+}
diff --git a/libopkg/pkg_alternatives.h b/libopkg/pkg_alternatives.h
new file mode 100644 (file)
index 0000000..25a5fba
--- /dev/null
@@ -0,0 +1,23 @@
+/* pkg_alternatives.c - the opkg package management system
+
+   Copyright (C) 2017 Yousong Zhou
+
+   This program 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 2, or (at
+   your option) any later version.
+
+   This program 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.
+*/
+
+#ifndef OPKG_ALTERNATIVES_H
+#define OPKG_ALTERNATIVES_H
+
+#include "pkg.h"
+
+int pkg_alternatives_update(pkg_t * pkg);
+
+#endif
index 121f147..d35770c 100644 (file)
@@ -112,6 +112,83 @@ static char *parse_architecture(pkg_t *pkg, const char *str)
        return pkg_set_architecture(pkg, s, e - s);
 }
 
+static void parse_alternatives(pkg_t *pkg, char *list)
+{
+       char *item, *tok;
+       struct pkg_alternatives *pkg_alts;
+       struct pkg_alternative **alts;
+       int nalts;
+
+       pkg_alts = pkg_get_ptr(pkg, PKG_ALTERNATIVES);
+       if (!pkg_alts) {
+               nalts = 0;
+               alts = NULL;
+       } else {
+               nalts = pkg_alts->nalts;
+               alts = pkg_alts->alts;
+       }
+
+       for (item = strtok_r(list, ",", &tok);
+                       item;
+                       item = strtok_r(NULL, ",", &tok)) {
+               enum pkg_alternative_field i;
+               char *val, *tok1;
+               /* the assignment was intended to quash the -Wmaybe-uninitialized warnings */
+               int prio = prio;
+               char *path = path, *altpath = altpath;
+
+               for (i = PAF_PRIO, val = strtok_r(item, ":", &tok1);
+                               val && i < __PAF_MAX;
+                               val = strtok_r(NULL, ":", &tok1), i++) {
+                       switch (i) {
+                               case PAF_PRIO:
+                                       prio = atoi(val);
+                                       break;
+                               case PAF_PATH:
+                                       path = val;
+                                       break;
+                               case PAF_ALTPATH:
+                                       altpath = val;
+                                       break;
+                               default:
+                                       break;
+                       }
+               }
+               if (!val && i == __PAF_MAX) {
+                       char *_path, *_altpath;
+                       struct pkg_alternative *alt;
+
+                       /*
+                        * - path must be absolute
+                        * - altpath must be non-empty
+                        */
+                       if (path[0] != '/' || !altpath[0])
+                               continue;
+
+                       alt = calloc_a(sizeof(*alt),
+                                       &_path, strlen(path) + 1,
+                                       &_altpath, strlen(altpath) + 1);
+                       if (!alt)
+                               continue;
+                       strcpy(_path, path);
+                       strcpy(_altpath, altpath);
+                       alt->prio = prio;
+                       alt->path = _path;
+                       alt->altpath = _altpath;
+                       alts = xrealloc(alts, sizeof(*alts) * (nalts + 1));
+                       alts[nalts++] = alt;
+               }
+       }
+
+       if (nalts > 0) {
+               if (!pkg_alts)
+                       pkg_alts = xmalloc(sizeof(*pkg_alts));
+               pkg_alts->nalts = nalts;
+               pkg_alts->alts = alts;
+               pkg_set_ptr(pkg, PKG_ALTERNATIVES, pkg_alts);
+       }
+}
+
 int pkg_parse_line(void *ptr, char *line, uint mask)
 {
        pkg_t *pkg = (pkg_t *) ptr;
@@ -131,7 +208,9 @@ int pkg_parse_line(void *ptr, char *line, uint mask)
 
        switch (*line) {
        case 'A':
-               if ((mask & PFM_ARCHITECTURE) && is_field("Architecture", line))
+               if ((mask & PFM_ALTERNATIVES) && is_field("Alternatives", line))
+                       parse_alternatives(pkg, line + strlen("Alternatives") + 1);
+               else if ((mask & PFM_ARCHITECTURE) && is_field("Architecture", line))
                        parse_architecture(pkg, line + strlen("Architecture") + 1);
                else if ((mask & PFM_AUTO_INSTALLED)
                           && is_field("Auto-Installed", line)) {
index ac8d7c0..d1f901a 100644 (file)
@@ -27,32 +27,33 @@ int pkg_parse_line(void *ptr, char *line, uint mask);
 #define EXCESSIVE_LINE_LEN     (4096 << 8)
 
 /* package field mask */
-#define PFM_ARCHITECTURE       (1 << 1)
-#define PFM_AUTO_INSTALLED     (1 << 2)
-#define PFM_CONFFILES          (1 << 3)
-#define PFM_CONFLICTS          (1 << 4)
-#define PFM_DESCRIPTION                (1 << 5)
-#define PFM_DEPENDS            (1 << 6)
-#define PFM_ESSENTIAL          (1 << 7)
-#define PFM_FILENAME           (1 << 8)
-#define PFM_INSTALLED_SIZE     (1 << 9)
-#define PFM_INSTALLED_TIME     (1 << 10)
-#define PFM_MD5SUM             (1 << 11)
-#define PFM_MAINTAINER         (1 << 12)
-#define PFM_PACKAGE            (1 << 13)
-#define PFM_PRIORITY           (1 << 14)
-#define PFM_PROVIDES           (1 << 15)
-#define PFM_PRE_DEPENDS                (1 << 16)
-#define PFM_RECOMMENDS         (1 << 17)
-#define PFM_REPLACES           (1 << 18)
-#define PFM_SECTION            (1 << 19)
-#define PFM_SHA256SUM          (1 << 20)
-#define PFM_SIZE               (1 << 21)
-#define PFM_SOURCE             (1 << 22)
-#define PFM_STATUS             (1 << 23)
-#define PFM_SUGGESTS           (1 << 24)
-#define PFM_TAGS               (1 << 25)
-#define PFM_VERSION            (1 << 26)
+#define PFM_ALTERNATIVES       (1 << 1)
+#define PFM_ARCHITECTURE       (1 << 2)
+#define PFM_AUTO_INSTALLED     (1 << 3)
+#define PFM_CONFFILES          (1 << 4)
+#define PFM_CONFLICTS          (1 << 5)
+#define PFM_DESCRIPTION                (1 << 6)
+#define PFM_DEPENDS            (1 << 7)
+#define PFM_ESSENTIAL          (1 << 8)
+#define PFM_FILENAME           (1 << 9)
+#define PFM_INSTALLED_SIZE     (1 << 10)
+#define PFM_INSTALLED_TIME     (1 << 11)
+#define PFM_MD5SUM             (1 << 12)
+#define PFM_MAINTAINER         (1 << 13)
+#define PFM_PACKAGE            (1 << 14)
+#define PFM_PRIORITY           (1 << 15)
+#define PFM_PROVIDES           (1 << 16)
+#define PFM_PRE_DEPENDS                (1 << 17)
+#define PFM_RECOMMENDS         (1 << 18)
+#define PFM_REPLACES           (1 << 19)
+#define PFM_SECTION            (1 << 20)
+#define PFM_SHA256SUM          (1 << 21)
+#define PFM_SIZE               (1 << 22)
+#define PFM_SOURCE             (1 << 23)
+#define PFM_STATUS             (1 << 24)
+#define PFM_SUGGESTS           (1 << 25)
+#define PFM_TAGS               (1 << 26)
+#define PFM_VERSION            (1 << 27)
 
 #define PFM_ALL        (~(uint)0)