diff options
author | Timo Teras <timo.teras@iki.fi> | 2009-06-25 15:14:07 +0300 |
---|---|---|
committer | Timo Teras <timo.teras@iki.fi> | 2009-06-25 15:14:07 +0300 |
commit | 7a29678aac20ac9e113704f8a5743f6051edef8d (patch) | |
tree | 11c9583a66fa5fc1122e6d993b92dc7cd591e237 /src/apk.c | |
parent | 3a488564753cee51832b6824128249a99eb4613b (diff) | |
download | apk-tools-7a29678aac20ac9e113704f8a5743f6051edef8d.tar.gz apk-tools-7a29678aac20ac9e113704f8a5743f6051edef8d.tar.bz2 apk-tools-7a29678aac20ac9e113704f8a5743f6051edef8d.tar.xz apk-tools-7a29678aac20ac9e113704f8a5743f6051edef8d.zip |
help: auto construct help
And add some more verbosity to the help message.
Diffstat (limited to 'src/apk.c')
-rw-r--r-- | src/apk.c | 188 |
1 files changed, 144 insertions, 44 deletions
@@ -4,7 +4,7 @@ * Copyright (C) 2008 Timo Teräs <timo.teras@iki.fi> * All rights reserved. * - * This program is free software; you can redistribute it and/or modify it + * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. See http://www.gnu.org/ for details. */ @@ -20,12 +20,29 @@ #include "apk_defines.h" #include "apk_applet.h" +#include "apk_blob.h" const char *apk_root; struct apk_repository_url apk_repository_list; int apk_verbosity = 1, apk_cwd_fd; unsigned int apk_flags = 0; +static struct apk_option generic_options[] = { + { 'h', "help", "Show generic help or applet specific help" }, + { 'p', "root", "Install packages to DIR", + required_argument, "DIR" }, + { 'X', "repository", "Use packages from REPO", + required_argument, "REPO" }, + { 'q', "quiet", "Print less information" }, + { 'v', "verbose", "Print more information" }, + { 'V', "version", "Print program version and exit" }, + { 'f', "force", "Do what was asked even if it looks dangerous" }, + { 0x101, "progress", "Show a progress bar" }, + { 0x102, "clean-protected", + "Do not create .apk-new files to configuration dirs" }, + { 0x104, "simulate", "Show what would be done without actually doing it" }, +}; + void apk_log(const char *prefix, const char *format, ...) { va_list va; @@ -38,34 +55,116 @@ void apk_log(const char *prefix, const char *format, ...) fprintf(stderr, "\n"); } -int version(void) +static int version(void) { printf("apk-tools " APK_VERSION "\n"); return 0; } -int generic_usage(void) +struct apk_indent { + int x; + int indent; +}; + +static int print_indented(struct apk_indent *i, apk_blob_t blob) { - struct apk_applet **a; - printf("usage: apk COMMAND [-h|--help] [-p|--root DIR] [-X|--repository REPO]\n" - "\t\t [-q|--quiet] [-v|--verbose] [-V|--version] [-f|--force]\n" - "\t\t [--progress] [--clean-protected] [--simulate] [ARGS]...\n\n" - "commands: "); + static const int wrap_length = 80; - for (a = &__start_apkapplets; a < &__stop_apkapplets; a++) { - printf("%s ", - (*a)->name); + if (i->x + blob.len + 1 >= wrap_length) { + i->x = i->indent; + printf("\n%*s", i->indent - 1, ""); } - printf("\n\n"); - return 1; + i->x += printf(" %.*s", blob.len, blob.ptr); + return 0; +} + +static void print_indented_words(struct apk_indent *i, const char *text) +{ + apk_blob_for_each_segment(APK_BLOB_STR(text), " ", + (apk_blob_cb) print_indented, i); } -int usage(struct apk_applet *applet) +static int format_option(char *buf, size_t len, struct apk_option *o, + const char *separator) { + int i = 0; + + if (o->val <= 0xff && isalnum(o->val)) { + i += snprintf(&buf[i], len - i, "-%c", o->val); + if (o->name != NULL) + i += snprintf(&buf[i], len - i, "%s", separator); + } + if (o->name != NULL) + i += snprintf(&buf[i], len - i, "--%s", o->name); + if (o->arg_name != NULL) + i += snprintf(&buf[i], len - i, " %s", o->arg_name); + + return i; +} + +static void print_usage(const char *cmd, const char *args, int num_opts, + struct apk_option *opts) +{ + struct apk_indent indent = { 0, 11 }; + char word[128]; + int i, j; + + indent.x = printf("\nusage: apk %s", cmd) - 1; + for (i = 0; i < num_opts; i++) { + j = 0; + word[j++] = '['; + j += format_option(&word[j], sizeof(word) - j, &opts[i], "|"); + word[j++] = ']'; + print_indented(&indent, APK_BLOB_PTR_LEN(word, j)); + } + if (args != NULL) + print_indented(&indent, APK_BLOB_STR(args)); + printf("\n"); +} + +static void print_options(int num_opts, struct apk_option *opts) +{ + struct apk_indent indent = { 0, 26 }; + char word[128]; + int i; + + for (i = 0; i < num_opts; i++) { + format_option(word, sizeof(word), &opts[i], ", "); + indent.x = printf(" %-*s", indent.indent - 3, word); + print_indented_words(&indent, opts[i].help); + printf("\n"); + } +} + +static int usage(struct apk_applet *applet) +{ + struct apk_indent indent = { 0, 2 }; + version(); - if (applet == NULL) - return generic_usage(); - printf("usage: apk %s %s\n\n", applet->name, applet->usage); + if (applet == NULL) { + struct apk_applet **a; + + print_usage("COMMAND", "[ARGS]...", + ARRAY_SIZE(generic_options), generic_options); + + printf("\navailable commands:\n "); + for (a = &__start_apkapplets; a < &__stop_apkapplets; a++) + printf("%s ", (*a)->name); + } else { + print_usage(applet->name, applet->arguments, + applet->num_options, applet->options); + printf("\ndescription:\n%*s", indent.indent - 1, ""); + print_indented_words(&indent, applet->help); + } + printf("\n\ngeneric options:\n"); + print_options(ARRAY_SIZE(generic_options), generic_options); + + if (applet != NULL && applet->num_options > 0) { + printf("\noptions for %s command:\n", applet->name); + print_options(applet->num_options, applet->options); + } + printf("\nThis apk has coffee making abilities.\n\n"); + return 1; } @@ -77,7 +176,7 @@ static struct apk_applet *find_applet(const char *name) if (strcmp(name, (*a)->name) == 0) return *a; } - + return NULL; } @@ -110,7 +209,7 @@ static struct apk_applet *deduce_applet(int argc, char **argv) static struct apk_repository_url *apk_repository_new(const char *url) { - struct apk_repository_url *r = calloc(1, + struct apk_repository_url *r = calloc(1, sizeof(struct apk_repository_url)); if (r) { r->url = url; @@ -119,26 +218,25 @@ static struct apk_repository_url *apk_repository_new(const char *url) return r; } -#define NUM_GENERIC_OPTS 10 -static struct option generic_options[32] = { - { "help", no_argument, NULL, 'h'}, - { "root", required_argument, NULL, 'p' }, - { "repository", required_argument, NULL, 'X' }, - { "quiet", no_argument, NULL, 'q' }, - { "verbose", no_argument, NULL, 'v' }, - { "version", no_argument, NULL, 'V' }, - { "force", no_argument, NULL, 'f' }, - { "progress", no_argument, NULL, 0x101 }, - { "clean-protected", no_argument, NULL, 0x102 }, - { "simulate", no_argument, NULL, 0x104 }, -}; +static void merge_options(struct option *opts, struct apk_option *ao, int num) +{ + int i; + + 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; + } + opts->name = NULL; +} int main(int argc, char **argv) { struct apk_applet *applet; char short_options[256], *sopt; - struct option *opt; - int r, optindex; + struct option *opt, *all_options; + int r, optindex, num_options; void *ctx = NULL; struct apk_repository_url *repo = NULL; @@ -148,19 +246,20 @@ int main(int argc, char **argv) list_init(&apk_repository_list.list); applet = deduce_applet(argc, argv); + num_options = ARRAY_SIZE(generic_options) + 1; + if (applet != NULL) + num_options += applet->num_options; + all_options = alloca(sizeof(struct option) * num_options); + merge_options(&all_options[0], generic_options, + ARRAY_SIZE(generic_options)); if (applet != NULL) { - if (applet->num_options && applet->options) { - memcpy(&generic_options[NUM_GENERIC_OPTS], - applet->options, - applet->num_options * sizeof(struct option)); - } - + merge_options(&all_options[ARRAY_SIZE(generic_options)], + applet->options, applet->num_options); if (applet->context_size != 0) ctx = calloc(1, applet->context_size); } - for (opt = &generic_options[0], sopt = short_options; - opt->name != NULL; opt++) { + for (opt = all_options, sopt = short_options; opt->name != NULL; opt++) { if (opt->flag == NULL && opt->val <= 0xff && isalnum(opt->val)) { *(sopt++) = opt->val; @@ -171,7 +270,7 @@ int main(int argc, char **argv) optindex = 0; while ((r = getopt_long(argc, argv, short_options, - generic_options, &optindex)) != -1) { + all_options, &optindex)) != -1) { switch (r) { case 0: break; @@ -208,7 +307,8 @@ int main(int argc, char **argv) break; default: if (applet == NULL || applet->parse == NULL || - applet->parse(ctx, r, optindex - NUM_GENERIC_OPTS, + applet->parse(ctx, r, + optindex - ARRAY_SIZE(generic_options), optarg) != 0) return usage(applet); break; |