summaryrefslogtreecommitdiff
path: root/src/apk.c
diff options
context:
space:
mode:
authorTimo Teräs <timo.teras@iki.fi>2020-05-04 21:45:11 +0300
committerTimo Teräs <timo.teras@iki.fi>2020-05-06 13:05:25 +0300
commit1d7123d83796182f851ccccaf056063955343718 (patch)
treeca675fd3363af3584694d0db5a3b65e618ae894b /src/apk.c
parent791f93fcbe7a543e0bb844887ba395be8ed8ea44 (diff)
downloadapk-tools-1d7123d83796182f851ccccaf056063955343718.tar.gz
apk-tools-1d7123d83796182f851ccccaf056063955343718.tar.bz2
apk-tools-1d7123d83796182f851ccccaf056063955343718.tar.xz
apk-tools-1d7123d83796182f851ccccaf056063955343718.zip
rewrite option descriptors to be single string
This reduces the number of relocations on PIE binaries, and also reduces the executable size. Parsing of the options is slightly sped up as only the exact matching option group parser is called.
Diffstat (limited to 'src/apk.c')
-rw-r--r--src/apk.c374
1 files changed, 212 insertions, 162 deletions
diff --git a/src/apk.c b/src/apk.c
index e993ac6..bb0dc79 100644
--- a/src/apk.c
+++ b/src/apk.c
@@ -13,6 +13,7 @@
#include <fcntl.h>
#include <ctype.h>
#include <errno.h>
+#include <assert.h>
#include <signal.h>
#include <stdarg.h>
#include <stdlib.h>
@@ -35,7 +36,6 @@
#include "apk_print.h"
#include "apk_io.h"
-static const struct apk_option_group *default_optgroups[] = { &optgroup_global, NULL };
static struct list_head apk_applet_list;
#define foreach_applet(iter) list_for_each_entry(iter, &apk_applet_list, node)
@@ -75,112 +75,185 @@ static struct apk_repository_list *apk_repository_new(const char *url)
return r;
}
-static int option_parse_global(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
+enum {
+ OPT_GLOBAL_allow_untrusted,
+ OPT_GLOBAL_arch,
+ OPT_GLOBAL_cache_dir,
+ OPT_GLOBAL_cache_max_age,
+ OPT_GLOBAL_force,
+ OPT_GLOBAL_force_binary_stdout,
+ OPT_GLOBAL_force_broken_world,
+ OPT_GLOBAL_force_non_repository,
+ OPT_GLOBAL_force_old_apk,
+ OPT_GLOBAL_force_overwrite,
+ OPT_GLOBAL_force_refresh,
+ OPT_GLOBAL_help,
+ OPT_GLOBAL_interactive,
+ OPT_GLOBAL_keys_dir,
+ OPT_GLOBAL_no_cache,
+ OPT_GLOBAL_no_network,
+ OPT_GLOBAL_no_progress,
+ OPT_GLOBAL_print_arch,
+ OPT_GLOBAL_progress,
+ OPT_GLOBAL_progress_fd,
+ OPT_GLOBAL_purge,
+ OPT_GLOBAL_quiet,
+ OPT_GLOBAL_repositories_file,
+ OPT_GLOBAL_repository,
+ OPT_GLOBAL_root,
+ OPT_GLOBAL_update_cache,
+ OPT_GLOBAL_verbose,
+ OPT_GLOBAL_version,
+ OPT_GLOBAL_wait,
+#ifdef TEST_MODE
+ OPT_GLOBAL_test_instdb,
+ OPT_GLOBAL_test_repo,
+ OPT_GLOBAL_test_world,
+#endif
+};
+
+static const char optiondesc_global[] =
+ APK_OPTGROUP("Global")
+ APK_OPT1n("allow-untrusted")
+ APK_OPT1R("arch")
+ APK_OPT1R("cache-dir")
+ APK_OPT1R("cache-max-age")
+ APK_OPT2n("force", "f")
+ APK_OPT1n("force-binary-stdout")
+ APK_OPT1n("force-broken-world")
+ APK_OPT1n("force-non-repository")
+ APK_OPT1n("force-old-apk")
+ APK_OPT1n("force-overwrite")
+ APK_OPT1n("force-refresh")
+ APK_OPT2n("help", "h")
+ APK_OPT2n("interactive", "i")
+ APK_OPT1R("keys-dir")
+ APK_OPT1n("no-cache")
+ APK_OPT1n("no-network")
+ APK_OPT1n("no-progress")
+ APK_OPT1n("print-arch")
+ APK_OPT1n("progress")
+ APK_OPT1R("progress-fd")
+ APK_OPT1n("purge")
+ APK_OPT2n("quiet", "q")
+ APK_OPT1R("repositories-file")
+ APK_OPT2R("repository", "X")
+ APK_OPT2R("root", "p")
+ APK_OPT2n("update-cache", "U")
+ APK_OPT2n("verbose", "v")
+ APK_OPT2n("version", "V")
+ APK_OPT1R("wait")
+#ifdef TEST_MODE
+ APK_OPT1R("test-instdb")
+ APK_OPT1R("test-repo")
+ APK_OPT1R("test-world")
+#endif
+ ;
+
+static int option_parse_global(void *ctx, struct apk_db_options *dbopts, int opt, const char *optarg)
{
struct apk_repository_list *repo;
- switch (optch) {
- case 'h': return -EINVAL;
- case 'p':
+ switch (opt) {
+ case OPT_GLOBAL_help:
+ return -EINVAL;
+ case OPT_GLOBAL_root:
dbopts->root = optarg;
break;
- case 0x107:
+ case OPT_GLOBAL_keys_dir:
dbopts->keys_dir = optarg;
break;
- case 0x108:
+ case OPT_GLOBAL_repositories_file:
dbopts->repositories_file = optarg;
break;
- case 'X':
+ case OPT_GLOBAL_repository:
repo = apk_repository_new(optarg);
if (repo) list_add(&repo->list, &dbopts->repository_list);
break;
- case 'q':
+ case OPT_GLOBAL_quiet:
apk_verbosity--;
break;
- case 'v':
+ case OPT_GLOBAL_verbose:
apk_verbosity++;
break;
- case 'V':
+ case OPT_GLOBAL_version:
version();
return -ESHUTDOWN;
- case 'f':
+ case OPT_GLOBAL_force:
apk_force |= APK_FORCE_OVERWRITE | APK_FORCE_OLD_APK
| APK_FORCE_BROKEN_WORLD | APK_FORCE_NON_REPOSITORY
| APK_FORCE_BINARY_STDOUT;
break;
- case 0x120:
+ case OPT_GLOBAL_force_overwrite:
apk_force |= APK_FORCE_OVERWRITE;
break;
- case 0x121:
+ case OPT_GLOBAL_force_old_apk:
apk_force |= APK_FORCE_OLD_APK;
break;
- case 0x122:
+ case OPT_GLOBAL_force_broken_world:
apk_force |= APK_FORCE_BROKEN_WORLD;
break;
- case 0x123:
+ case OPT_GLOBAL_force_refresh:
apk_force |= APK_FORCE_REFRESH;
break;
- case 0x124:
+ case OPT_GLOBAL_force_non_repository:
apk_force |= APK_FORCE_NON_REPOSITORY;
break;
- case 0x125:
+ case OPT_GLOBAL_force_binary_stdout:
apk_force |= APK_FORCE_BINARY_STDOUT;
break;
- case 'i':
+ case OPT_GLOBAL_interactive:
apk_flags |= APK_INTERACTIVE;
break;
- case 0x101:
+ case OPT_GLOBAL_progress:
apk_flags |= APK_PROGRESS;
break;
- case 0x104:
- apk_flags |= APK_SIMULATE;
- break;
- case 0x110:
+ case OPT_GLOBAL_no_progress:
apk_flags &= ~APK_PROGRESS;
break;
- case 0x10f:
+ case OPT_GLOBAL_progress_fd:
apk_progress_fd = atoi(optarg);
break;
- case 0x103:
+ case OPT_GLOBAL_allow_untrusted:
apk_flags |= APK_ALLOW_UNTRUSTED;
break;
- case 0x106:
+ case OPT_GLOBAL_purge:
apk_flags |= APK_PURGE;
break;
- case 0x105:
+ case OPT_GLOBAL_wait:
dbopts->lock_wait = atoi(optarg);
break;
- case 0x109:
+ case OPT_GLOBAL_no_network:
apk_flags |= APK_NO_NETWORK;
break;
- case 0x115:
+ case OPT_GLOBAL_no_cache:
apk_flags |= APK_NO_CACHE;
break;
- case 0x116:
+ case OPT_GLOBAL_cache_dir:
dbopts->cache_dir = optarg;
break;
- case 'U':
+ case OPT_GLOBAL_update_cache:
/* Make it one minute, to avoid updating indexes twice
* when doing self-upgrade's re-exec */
dbopts->cache_max_age = 60;
break;
- case 0x119:
+ case OPT_GLOBAL_cache_max_age:
dbopts->cache_max_age = atoi(optarg) * 60;
break;
- case 0x112:
+ case OPT_GLOBAL_arch:
dbopts->arch = optarg;
break;
- case 0x114:
+ case OPT_GLOBAL_print_arch:
puts(APK_DEFAULT_ARCH);
return -ESHUTDOWN;
#ifdef TEST_MODE
- case 0x200:
+ case OPT_GLOBAL_test_repo:
*apk_string_array_add(&test_repos) = (char*) optarg;
break;
- case 0x201:
+ case OPT_GLOBAL_test_instdb:
test_installed_db = optarg;
break;
- case 0x202:
+ case OPT_GLOBAL_test_world:
test_world = optarg;
break;
#endif
@@ -190,69 +263,48 @@ static int option_parse_global(void *ctx, struct apk_db_options *dbopts, int opt
return 0;
}
-static const struct apk_option options_global[] = {
- { 'h', "help" },
- { 'p', "root", required_argument },
- { 'X', "repository", required_argument },
- { 'q', "quiet" },
- { 'v', "verbose" },
- { 'i', "interactive" },
- { 'V', "version" },
- { 'f', "force" },
- { 0x125, "force-binary-stdout" },
- { 0x122, "force-broken-world" },
- { 0x124, "force-non-repository" },
- { 0x121, "force-old-apk" },
- { 0x120, "force-overwrite" },
- { 0x123, "force-refresh" },
- { 'U', "update-cache" },
- { 0x101, "progress" },
- { 0x10f, "progress-fd", required_argument },
- { 0x110, "no-progress" },
- { 0x106, "purge" },
- { 0x103, "allow-untrusted" },
- { 0x105, "wait", required_argument },
- { 0x107, "keys-dir", required_argument },
- { 0x108, "repositories-file", required_argument },
- { 0x109, "no-network" },
- { 0x115, "no-cache" },
- { 0x116, "cache-dir", required_argument },
- { 0x119, "cache-max-age", required_argument },
- { 0x112, "arch", required_argument },
- { 0x114, "print-arch" },
-#ifdef TEST_MODE
- { 0x200, "test-repo", required_argument },
- { 0x201, "test-instdb", required_argument },
- { 0x202, "test-world", required_argument },
-#endif
-};
-
const struct apk_option_group optgroup_global = {
- .name = "Global",
- .options = options_global,
- .num_options = ARRAY_SIZE(options_global),
+ .desc = optiondesc_global,
.parse = option_parse_global,
};
-static int option_parse_commit(void *ctx, struct apk_db_options *dbopts, int optch, const char *optarg)
+enum {
+ OPT_COMMIT_clean_protected,
+ OPT_COMMIT_initramfs_diskless_boot,
+ OPT_COMMIT_no_commit_hooks,
+ OPT_COMMIT_no_scripts,
+ OPT_COMMIT_overlay_from_stdin,
+ OPT_COMMIT_simulate,
+};
+
+static const char optiondesc_commit[] =
+ APK_OPTGROUP("commit")
+ APK_OPT1n("clean-protected")
+ APK_OPT1n("initramfs-diskless-boot")
+ APK_OPT1n("no-commit-hooks")
+ APK_OPT1n("no-scripts")
+ APK_OPT1n("overlay-from-stdin")
+ APK_OPT2n("simulate", "s");
+
+static int option_parse_commit(void *ctx, struct apk_db_options *dbopts, int opt, const char *optarg)
{
- switch (optch) {
- case 's':
+ switch (opt) {
+ case OPT_COMMIT_simulate:
apk_flags |= APK_SIMULATE;
break;
- case 0x102:
+ case OPT_COMMIT_clean_protected:
apk_flags |= APK_CLEAN_PROTECTED;
break;
- case 0x111:
+ case OPT_COMMIT_overlay_from_stdin:
apk_flags |= APK_OVERLAY_FROM_STDIN;
break;
- case 0x113:
+ case OPT_COMMIT_no_scripts:
apk_flags |= APK_NO_SCRIPTS;
break;
- case 0x117:
+ case OPT_COMMIT_no_commit_hooks:
apk_flags |= APK_NO_COMMIT_HOOKS;
break;
- case 0x118:
+ case OPT_COMMIT_initramfs_diskless_boot:
dbopts->open_flags |= APK_OPENF_CREATE;
apk_flags |= APK_NO_COMMIT_HOOKS;
apk_force |= APK_FORCE_OVERWRITE | APK_FORCE_OLD_APK
@@ -264,19 +316,8 @@ static int option_parse_commit(void *ctx, struct apk_db_options *dbopts, int opt
return 0;
}
-static const struct apk_option options_commit[] = {
- { 's', "simulate" },
- { 0x102, "clean-protected" },
- { 0x111, "overlay-from-stdin" },
- { 0x113, "no-scripts" },
- { 0x117, "no-commit-hooks" },
- { 0x118, "initramfs-diskless-boot" },
-};
-
const struct apk_option_group optgroup_commit = {
- .name = "Commit",
- .options = options_commit,
- .num_options = ARRAY_SIZE(options_commit),
+ .desc = optiondesc_commit,
.parse = option_parse_commit,
};
@@ -315,28 +356,81 @@ static struct apk_applet *deduce_applet(int argc, char **argv)
return find_applet(prog + 4);
for (i = 1; i < argc; i++) {
- if (argv[i][0] == '-')
- continue;
-
+ if (argv[i][0] == '-') continue;
a = find_applet(argv[i]);
- if (a != NULL)
- return a;
+ if (a) return a;
}
return NULL;
}
-static void merge_options(struct option *opts, const struct apk_option *ao, int num)
+static int parse_options(int argc, char **argv, struct apk_applet *applet, void *ctx, struct apk_db_options *dbopts)
{
- int i;
+ const struct apk_option_group *default_optgroups[] = { &optgroup_global, NULL };
+ const struct apk_option_group *og, **optgroups = default_optgroups;
+ struct option all_options[80], *opt;
+ char short_options[256], *sopt;
+ unsigned short short_option_val[64];
+ int r, p, help_requested = 0, num_short;
+
+ memset(short_option_val, 0, sizeof short_option_val);
- for (i = 0; i < num; i++, opts++, ao++) {
- opts->name = ao->name ?: "";
- opts->has_arg = ao->has_arg;
- opts->flag = NULL;
- opts->val = ao->val;
+ if (applet && applet->optgroups[0]) optgroups = applet->optgroups;
+
+ for (p = 0, opt = &all_options[0], sopt = short_options; (og = optgroups[p]) != 0; p++) {
+ assert(opt < &all_options[ARRAY_SIZE(all_options)]);
+ assert(sopt < &short_options[sizeof short_options]);
+ const char *d = og->desc + strlen(og->desc) + 1;
+ for (r = 0; *d; r++) {
+ opt->val = (p << 10) + r;
+ opt->flag = 0;
+ opt->has_arg = no_argument;
+ if ((unsigned char)*d == 0xaf) {
+ opt->has_arg = required_argument;
+ d++;
+ }
+ num_short = 1;
+ if ((unsigned char)*d >= 0xf0)
+ num_short = *d++ & 0x0f;
+ for (; num_short > 0; num_short--) {
+ assert(*d >= 64 && *d < 128);
+ short_option_val[*d - 64] = opt->val;
+ *sopt++ = *d++;
+ if (opt->has_arg != no_argument)
+ *sopt++ = ':';
+ }
+ opt->name = d;
+ opt++;
+ d += strlen(d) + 1;
+ }
}
- opts->name = NULL;
+ opt->name = 0;
+ *sopt = 0;
+
+ r = 0;
+ while ((p = getopt_long(argc, argv, short_options, all_options, NULL)) != -1) {
+ if (p >= 64 && p < 128) p = short_option_val[p - 64];
+ og = optgroups[p >> 10];
+ r = og->parse(ctx, dbopts, p & 0x3ff, optarg);
+ if (r == 0) continue;
+ if (r == -EINVAL) {
+ help_requested = 1;
+ continue;
+ }
+ if (r != -ENOTSUP) return r;
+ }
+
+ if (help_requested || r == -ENOTSUP)
+ return usage(applet);
+
+ if (applet == NULL) {
+ if (argc > 1) {
+ apk_error("'%s' is not an apk command. See 'apk --help'.", argv[1]);
+ return 1;
+ }
+ return usage(NULL);
+ }
+ return 0;
}
static void fini_openssl(void)
@@ -407,14 +501,11 @@ static void on_sigint(int s)
int main(int argc, char **argv)
{
- struct apk_applet *applet;
- char short_options[256], *sopt;
- struct option *opt, *all_options;
- int i, p, r, num_options, help_requested = 0;
void *ctx = NULL;
struct apk_db_options dbopts;
- const struct apk_option_group **optgroups = default_optgroups;
struct apk_string_array *args;
+ struct apk_applet *applet;
+ int r;
apk_string_array_init(&args);
#ifdef TEST_MODE
@@ -434,13 +525,6 @@ int main(int argc, char **argv)
setup_terminal();
applet = deduce_applet(argc, argv);
- if (applet && applet->optgroups[0]) optgroups = applet->optgroups;
-
- for (i = 0, num_options = 1; optgroups[i]; i++)
- num_options += optgroups[i]->num_options;
- all_options = alloca(sizeof(struct option) * num_options);
- for (i = r = 0; optgroups[i]; r += optgroups[i]->num_options, i++)
- merge_options(&all_options[r], optgroups[i]->options, optgroups[i]->num_options);
if (applet != NULL) {
if (applet->context_size != 0)
ctx = calloc(1, applet->context_size);
@@ -448,45 +532,13 @@ int main(int argc, char **argv)
apk_flags |= applet->forced_flags;
apk_force |= applet->forced_force;
}
- for (opt = all_options, sopt = short_options; opt->name != NULL; opt++) {
- if (opt->flag == NULL &&
- opt->val <= 0xff && isalnum(opt->val)) {
- *(sopt++) = opt->val;
- if (opt->has_arg != no_argument)
- *(sopt++) = ':';
- }
- }
- *(sopt++) = 0;
init_openssl();
setup_automatic_flags();
fetchConnectionCacheInit(32, 4);
- while ((p = getopt_long(argc, argv, short_options, all_options, NULL)) != -1) {
- for (i = 0; optgroups[i]; i++) {
- r = optgroups[i]->parse(ctx, &dbopts, p, optarg);
- if (r == 0) break;
- if (r == -EINVAL) {
- help_requested = 1;
- break;
- }
- if (r != -ENOTSUP) goto err;
- }
- }
-
- if (help_requested || r == -ENOTSUP) {
- r = usage(applet);
- goto err;
- }
-
- if (applet == NULL) {
- if (argc > 1) {
- r = 1;
- apk_error("'%s' is not an apk command. See 'apk --help'.", argv[1]);
- } else
- r = usage(NULL);
- goto err;
- }
+ r = parse_options(argc, argv, applet, ctx, &dbopts);
+ if (r != 0) goto err;
argc -= optind;
argv += optind;
@@ -519,7 +571,7 @@ int main(int argc, char **argv)
if (test_installed_db != NULL) {
apk_db_index_read(&db, apk_istream_from_file(AT_FDCWD, test_installed_db), -1);
}
- for (i = 0; i < test_repos->num; i++) {
+ for (int i = 0; i < test_repos->num; i++) {
apk_blob_t spec = APK_BLOB_STR(test_repos->item[i]), name, tag;
int repo_tag = 0, repo = APK_REPOSITORY_FIRST_CONFIGURED + i;
@@ -561,10 +613,8 @@ int main(int argc, char **argv)
#endif
err:
- if (r == -ESHUTDOWN)
- r = 0;
- if (ctx)
- free(ctx);
+ if (r == -ESHUTDOWN) r = 0;
+ if (ctx) free(ctx);
fetchConnectionCacheClose();
apk_string_array_free(&args);