From: Yousong Zhou Date: Sat, 18 Mar 2017 08:45:53 +0000 (+0800) Subject: pkg: alternatives support X-Git-Url: http://git.openwrt.org/?p=project%2Fopkg-lede.git;a=commitdiff_plain;h=546bc72356c7a6b435540852b10625b7531f2117 pkg: alternatives support It's a list of specs of the following form seprated by commas to describe alternatives provided by the package :: where will be a symbolic link to of the highest 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 --- diff --git a/libopkg/CMakeLists.txt b/libopkg/CMakeLists.txt index 637dadb..1b2a949 100644 --- a/libopkg/CMakeLists.txt +++ b/libopkg/CMakeLists.txt @@ -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 diff --git a/libopkg/opkg_configure.c b/libopkg/opkg_configure.c index dc05f1e..a043c52 100644 --- a/libopkg/opkg_configure.c +++ b/libopkg/opkg_configure.c @@ -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; } diff --git a/libopkg/opkg_remove.c b/libopkg/opkg_remove.c index 694c3f3..96ca558 100644 --- a/libopkg/opkg_remove.c +++ b/libopkg/opkg_remove.c @@ -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; diff --git a/libopkg/pkg.c b/libopkg/pkg.c index 551c629..21f239e 100644 --- a/libopkg/pkg.c +++ b/libopkg/pkg.c @@ -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); } diff --git a/libopkg/pkg.h b/libopkg/pkg.h index cf405b1..600fc9e 100644 --- a/libopkg/pkg.h +++ b/libopkg/pkg.h @@ -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 index 0000000..66b64de --- /dev/null +++ b/libopkg/pkg_alternatives.c @@ -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 +#include /* stat */ +#include +#include + +#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 index 0000000..25a5fba --- /dev/null +++ b/libopkg/pkg_alternatives.h @@ -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 diff --git a/libopkg/pkg_parse.c b/libopkg/pkg_parse.c index 121f147..d35770c 100644 --- a/libopkg/pkg_parse.c +++ b/libopkg/pkg_parse.c @@ -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)) { diff --git a/libopkg/pkg_parse.h b/libopkg/pkg_parse.h index ac8d7c0..d1f901a 100644 --- a/libopkg/pkg_parse.h +++ b/libopkg/pkg_parse.h @@ -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)