help: auto construct help

And add some more verbosity to the help message.
cute-signatures
Timo Teras 2009-06-25 15:14:07 +03:00
parent 3a48856475
commit 7a29678aac
13 changed files with 265 additions and 143 deletions

View File

@ -51,45 +51,37 @@ static void md5_str(const char *str, md5sum_t csum)
static int cup(void)
{
/* compressed/uncompressed size is 259/1213 */
static unsigned char z[] = {
0x78,0x9c,0x9d,0x94,0x3d,0x8e,0xc4,0x20,
0xc,0x85,0xfb,0x9c,0xc2,0x72,0x43,0x46,
0x8a,0x4d,0x3f,0x67,0x89,0x64,0x77,0x2b,
0x6d,0xbb,0x6d,0xe,0x3f,0xc6,0x84,0x4d,
0x8,0x84,0x55,0xd6,0xa2,0xe0,0xef,0x7b,
0x36,0xe1,0x11,0x80,0x6e,0xcc,0x53,0x7f,
0x3e,0xc5,0xeb,0xcf,0x1d,0x20,0x22,0xcc,
0x3c,0x53,0x8e,0x17,0xd9,0x80,0x6d,0xee,
0xe,0x61,0x42,0x3c,0x8b,0xcf,0xc7,0x12,
0x22,0x71,0x8b,0x31,0x5,0xd5,0xb0,0x11,
0x4b,0xa7,0x32,0x2f,0x80,0x69,0x6b,0xb0,
0x98,0x40,0xe2,0xcd,0xba,0x6a,0xba,0xe4,
0x65,0xed,0x61,0x23,0x44,0xb5,0x95,0x6,
0x8b,0xde,0x6c,0x61,0x70,0xde,0xe,0xb6,
0xed,0xc4,0x43,0xc,0x56,0x6f,0x8f,0x31,
0xd0,0x35,0xb5,0xc7,0x58,0x6,0xff,0x81,
0x49,0x84,0xb8,0xe,0xb1,0xd8,0xc1,0x66,
0x31,0xe,0x46,0x5c,0x43,0xc9,0xef,0xe5,
0xdc,0x63,0xb1,0xdc,0x67,0x6d,0x31,0xb3,
0xc9,0x69,0x74,0x87,0xc7,0xa3,0x1b,0x6a,
0xb3,0xbd,0x2f,0x3b,0xd5,0xc,0x57,0x3b,
0xce,0x7c,0x5e,0xe5,0x48,0xd0,0x48,0x1,
0x92,0x49,0x8b,0xf7,0xfc,0x58,0x67,0xb3,
0xf7,0x14,0x20,0x5c,0x4c,0x9e,0xcc,0xeb,
0x78,0x7e,0x64,0xa6,0xa1,0xf5,0xb2,0x70,
0x38,0x9,0x7c,0x7f,0xfd,0xc0,0x8a,0x4e,
0xc8,0x55,0xe8,0x12,0xe2,0x9f,0x1a,0xb1,
0xb9,0x82,0x52,0x2,0x7a,0xe5,0xf9,0xd9,
0x88,0x47,0x79,0x3b,0x46,0x61,0x27,0xf9,
0x51,0xb1,0x17,0xb0,0x2c,0xe,0xd5,0x39,
0x2d,0x96,0x25,0x27,0xd6,0xd1,0x3f,0xa5,
0x8,0xe1,0x9e,0x4e,0xa7,0xe9,0x3,0xb1,
0xa,0xb6,0x75,};
/* compressed/uncompressed size is 259/1213 */
static unsigned char z[] = {
0x78,0x9c,0x9d,0x94,0x3d,0x8e,0xc4,0x20,0x0c,0x85,0xfb,0x9c,
0xc2,0x72,0x43,0x46,0x8a,0x4d,0x3f,0x67,0x89,0x64,0x77,0x2b,
0x6d,0xbb,0x6d,0x0e,0x3f,0xc6,0x84,0x4d,0x08,0x84,0x55,0xd6,
0xa2,0xe0,0xef,0x7b,0x36,0xe1,0x11,0x80,0x6e,0xcc,0x53,0x7f,
0x3e,0xc5,0xeb,0xcf,0x1d,0x20,0x22,0xcc,0x3c,0x53,0x8e,0x17,
0xd9,0x80,0x6d,0xee,0x0e,0x61,0x42,0x3c,0x8b,0xcf,0xc7,0x12,
0x22,0x71,0x8b,0x31,0x05,0xd5,0xb0,0x11,0x4b,0xa7,0x32,0x2f,
0x80,0x69,0x6b,0xb0,0x98,0x40,0xe2,0xcd,0xba,0x6a,0xba,0xe4,
0x65,0xed,0x61,0x23,0x44,0xb5,0x95,0x06,0x8b,0xde,0x6c,0x61,
0x70,0xde,0x0e,0xb6,0xed,0xc4,0x43,0x0c,0x56,0x6f,0x8f,0x31,
0xd0,0x35,0xb5,0xc7,0x58,0x06,0xff,0x81,0x49,0x84,0xb8,0x0e,
0xb1,0xd8,0xc1,0x66,0x31,0x0e,0x46,0x5c,0x43,0xc9,0xef,0xe5,
0xdc,0x63,0xb1,0xdc,0x67,0x6d,0x31,0xb3,0xc9,0x69,0x74,0x87,
0xc7,0xa3,0x1b,0x6a,0xb3,0xbd,0x2f,0x3b,0xd5,0x0c,0x57,0x3b,
0xce,0x7c,0x5e,0xe5,0x48,0xd0,0x48,0x01,0x92,0x49,0x8b,0xf7,
0xfc,0x58,0x67,0xb3,0xf7,0x14,0x20,0x5c,0x4c,0x9e,0xcc,0xeb,
0x78,0x7e,0x64,0xa6,0xa1,0xf5,0xb2,0x70,0x38,0x09,0x7c,0x7f,
0xfd,0xc0,0x8a,0x4e,0xc8,0x55,0xe8,0x12,0xe2,0x9f,0x1a,0xb1,
0xb9,0x82,0x52,0x02,0x7a,0xe5,0xf9,0xd9,0x88,0x47,0x79,0x3b,
0x46,0x61,0x27,0xf9,0x51,0xb1,0x17,0xb0,0x2c,0x0e,0xd5,0x39,
0x2d,0x96,0x25,0x27,0xd6,0xd1,0x3f,0xa5,0x08,0xe1,0x9e,0x4e,
0xa7,0xe9,0x03,0xb1,0x0a,0xb6,0x75
};
unsigned char buf[1213];
unsigned long len = 1213; //sizeof(buf);
unsigned long len = sizeof(buf);
uncompress(buf, &len, z, sizeof(z));
write(STDOUT_FILENO, buf, len);
return 0;
}
@ -174,15 +166,21 @@ err:
return r;
}
static struct option add_options[] = {
{ "initdb", no_argument, NULL, 0x10000 },
{ "upgrade", no_argument, NULL, 'u' },
{ "virtual", required_argument, NULL, 't' },
static struct apk_option add_options[] = {
{ 0x10000, "initdb", "Initialize database" },
{ 'u', "upgrade", "Prefer to upgrade package" },
{ 't', "virtual",
"Instead of adding all the packages to 'world', create a new virtual "
"package with the listed dependencies and add that to 'world'. The "
"actions of the command are easily reverted by deleting the virtual "
"package.", required_argument, "NAME" },
};
static struct apk_applet apk_add = {
.name = "add",
.usage = "[--initdb] [-u|--upgrade] [-t|--virtual NAME] PACKAGE...",
.help = "Add (or update) PACKAGEs to main dependencies and install "
"them, while ensuring that all dependencies are met.",
.arguments = "PACKAGE...",
.context_size = sizeof(struct add_ctx),
.num_options = ARRAY_SIZE(add_options),
.options = add_options,

184
src/apk.c
View File

@ -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_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: ");
struct apk_indent {
int x;
int indent;
};
for (a = &__start_apkapplets; a < &__stop_apkapplets; a++) {
printf("%s ",
(*a)->name);
static int print_indented(struct apk_indent *i, apk_blob_t blob)
{
static const int wrap_length = 80;
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;
}
int usage(struct apk_applet *applet)
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);
}
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;
}
@ -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;

View File

@ -24,13 +24,22 @@ struct apk_repository_url {
extern struct apk_repository_url apk_repository_list;
struct apk_option {
int val;
const char *name;
const char *help;
int has_arg;
const char *arg_name;
};
struct apk_applet {
const char *name;
const char *usage;
const char *arguments;
const char *help;
int context_size;
int num_options;
struct option *options;
struct apk_option *options;
int (*parse)(void *ctx, int optch, int optindex, const char *optarg);
int (*main)(void *ctx, int argc, char **argv);

View File

@ -19,6 +19,7 @@ struct apk_blob {
char *ptr;
};
typedef struct apk_blob apk_blob_t;
typedef int (*apk_blob_cb)(void *ctx, apk_blob_t blob);
#define APK_BLOB_IS_NULL(blob) (blob.ptr == NULL)
@ -44,7 +45,7 @@ unsigned long apk_blob_hash(apk_blob_t str);
int apk_blob_compare(apk_blob_t a, apk_blob_t b);
int apk_blob_for_each_segment(apk_blob_t blob, const char *split,
int (*cb)(void *ctx, apk_blob_t blob), void *ctx);
apk_blob_cb cb, void *ctx);
int apk_hexdump_parse(apk_blob_t to, apk_blob_t from);
int apk_hexdump_format(int tolen, char *to, apk_blob_t from);

View File

@ -128,13 +128,15 @@ static int audit_main(void *ctx, int argc, char **argv)
return r;
}
static struct option audit_options[] = {
{ "backup", no_argument, NULL, 0x10000 },
static struct apk_option audit_options[] = {
{ 0x10000, "backup",
"List all modified configuration files that need to be backed up" },
};
static struct apk_applet apk_audit = {
.name = "audit",
.usage = "--backup",
.help = "Audit the filesystem for changes compared to installed "
"database.",
.context_size = sizeof(struct audit_ctx),
.num_options = ARRAY_SIZE(audit_options),
.options = audit_options,

View File

@ -60,7 +60,8 @@ out:
static struct apk_applet apk_del = {
.name = "del",
.usage = "PACKAGE...",
.help = "Remove PACKAGEs from the main dependencies and uninstall them.",
.arguments = "PACKAGE...",
.main = del_main,
};

View File

@ -184,17 +184,20 @@ err:
return r;
}
static struct option fetch_options[] = {
{ "recursive", no_argument, NULL, 'R' },
{ "stdout", no_argument, NULL, 's' },
{ "link", no_argument, NULL, 'L' },
{ "output", required_argument, NULL, 'o' },
static struct apk_option fetch_options[] = {
{ 'l', "link", "Create hard links if possible" },
{ 'R', "recursive", "Fetch the PACKAGE and all it's dependencies" },
{ 's', "stdout",
"Dump the .apk to stdout (incompatible with -o and -R)" },
{ 'o', "output", "Directory to place the PACKAGEs to",
required_argument, "DIR" },
};
static struct apk_applet apk_fetch = {
.name = "fetch",
.usage = "[-R|--recursive|--stdout] [-L|--link] [-o|--output DIR]\n"
"\t\t PACKAGE...",
.help = "Download PACKAGEs from repositories to a local directory from "
"which a local mirror repository can be created.",
.arguments = "PACKAGE...",
.context_size = sizeof(struct fetch_ctx),
.num_options = ARRAY_SIZE(fetch_options),
.options = fetch_options,

View File

@ -113,13 +113,16 @@ static int index_main(void *ctx, int argc, char **argv)
return 0;
}
static struct option index_options[] = {
{ "delete", required_argument, NULL, 'd' },
static struct apk_option index_options[] = {
{ 'd', "delete",
"Read existing INDEXFILE and delete the listed FILEs from it",
required_argument, "INDEXFILE" },
};
static struct apk_applet apk_index = {
.name = "index",
.usage = "[-d|--delete INDEXFILE] FILE...",
.help = "Create repository index file from FILEs.",
.arguments = "FILE...",
.context_size = sizeof(struct index_ctx),
.num_options = ARRAY_SIZE(index_options),
.options = index_options,

View File

@ -325,21 +325,21 @@ static int info_main(void *ctx, int argc, char **argv)
return r;
}
static struct option info_options[] = {
{ "contents", no_argument, NULL, 'L' },
{ "installed", no_argument, NULL, 'e' },
{ "who-owns", no_argument, NULL, 'W' },
{ "depends", no_argument, NULL, 'R' },
{ "rdepends", no_argument, NULL, 'r' },
{ "webpage", no_argument, NULL, 'w' },
{ "size", no_argument, NULL, 's' },
{ "description", no_argument, NULL, 'd' },
static struct apk_option info_options[] = {
{ 'L', "contents", "List contents of the PACKAGE" },
{ 'e', "installed", "Check if PACKAGE is installed" },
{ 'W', "who-owns", "Print the package owning the specified file" },
{ 'R', "depends", "List packages that the PACKAGE depends on" },
{ 'r', "rdepends", "List all packages depending on PACKAGE" },
{ 'w', "webpage", "Show URL for more information about PACKAGE" },
{ 's', "size", "Show installed size of PACKAGE" },
{ 'd', "description", "Print description for PACKAGE" },
};
static struct apk_applet apk_info = {
.name = "info",
.usage = "[-L|--contents] [-e|--installed] [-W|--who-owns] [-R|--depends]\n"
"\t\t[-r|--rdepends] [-w|--webage] [-s|--size] PACKAGE...",
.help = "Give detailed information about PACKAGEs.",
.arguments = "PACKAGE...",
.context_size = sizeof(struct info_ctx),
.num_options = ARRAY_SIZE(info_options),
.options = info_options,

View File

@ -130,13 +130,14 @@ static int search_main(void *ctx, int argc, char **argv)
return r;
}
static struct option search_options[] = {
{ "description", no_argument, NULL, 'd' },
static struct apk_option search_options[] = {
{ 'd', "description", "Search also package descriptions" },
};
static struct apk_applet apk_search = {
.name = "search",
.usage = "[--description|-d] PATTERN",
.help = "Search package names (and descriptions) by wildcard PATTERN.",
.arguments = "PATTERN",
.context_size = sizeof(struct search_ctx),
.num_options = ARRAY_SIZE(search_options),
.options = search_options,

View File

@ -30,7 +30,7 @@ static int update_main(void *ctx, int argc, char **argv)
static struct apk_applet apk_update = {
.name = "update",
.usage = "",
.help = "Update repository indexes from all remote repositories.",
.main = update_main,
};

View File

@ -57,13 +57,16 @@ err:
return r;
}
static struct option upgrade_options[] = {
{ "available", no_argument, NULL, 'a' },
static struct apk_option upgrade_options[] = {
{ 'a', "available",
"Re-install or downgrade if currently installed package is not "
"currently available from any repository" },
};
static struct apk_applet apk_upgrade = {
.name = "upgrade",
.usage = "[-a|--available]",
.help = "Upgrade (or downgrade with -a) the currently installed "
"packages to versions available in repositories.",
.num_options = ARRAY_SIZE(upgrade_options),
.options = upgrade_options,
.parse = upgrade_parse,

View File

@ -137,14 +137,15 @@ ver_exit:
return ret;
}
static struct option ver_options[] = {
{ "test", no_argument, NULL, 't' },
{ "check", no_argument, NULL, 'c' },
static struct apk_option ver_options[] = {
{ 't', "test", "Compare two given versions" },
{ 'c', "check", "Check if the given version string is valid" },
};
static struct apk_applet apk_ver = {
.name = "version",
.usage = "[-t|--test version1 version2] [-c|--check]",
.help = "Compare package versions (in installed database vs. available)"
" or do tests on version strings given on command line.",
.context_size = sizeof(struct ver_ctx),
.num_options = ARRAY_SIZE(ver_options),
.options = ver_options,