modinfo: fix "-F firmware", add "intree" field display

function                                             old     new   delta
shortcuts                                              -      52     +52
modinfo                                              317     330     +13
display                                               77      87     +10
packed_usage                                       30752   30761      +9
modinfo_main                                         351     345      -6
static.shortcuts                                      48       -     -48
------------------------------------------------------------------------------
(add/remove: 1/1 grow/shrink: 3/1 up/down: 84/-54)             Total: 30 bytes

Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/modutils/modinfo.c b/modutils/modinfo.c
index 8e74b64..aa641ad 100644
--- a/modutils/modinfo.c
+++ b/modutils/modinfo.c
@@ -5,11 +5,6 @@
  *
  * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  */
-
-//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP))
-
-//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
-
 //config:config MODINFO
 //config:	bool "modinfo"
 //config:	default y
@@ -17,26 +12,46 @@
 //config:	help
 //config:	  Show information about a Linux Kernel module
 
+//applet:IF_MODINFO(APPLET(modinfo, BB_DIR_SBIN, BB_SUID_DROP))
+
+//kbuild:lib-$(CONFIG_MODINFO) += modinfo.o modutils.o
+
 #include <fnmatch.h>
 #include <sys/utsname.h> /* uname() */
 #include "libbb.h"
 #include "modutils.h"
 
+static const char *const shortcuts[] = {
+	"filename",	// -n
+	"author",	// -a
+	"description",	// -d
+	"license",	// -l
+	"parm",		// -p
+	"version",	// the rest has no shortcut options
+	"alias",
+	"srcversion",
+	"depends",
+	"uts_release",
+	"intree",
+	"vermagic",
+	"firmware",
+};
 
 enum {
-	OPT_TAGS = (1 << 12) - 1, /* shortcut count */
-	OPT_F = (1 << 12), /* field name */
-	OPT_0 = (1 << 13), /* \0 as separator */
+	OPT_0 = (1 << 0), /* \0 as separator */
+	OPT_F = (1 << 1), /* field name */
+	/* first bits are for -nadlp options, the rest are for
+	 * fields not selectable with "shortcut" options
+	 */
+	OPT_n = (1 << 2),
+	OPT_TAGS = ((1 << ARRAY_SIZE(shortcuts)) - 1) << 2,
 };
 
-struct modinfo_env {
-	char *field;
-	int tags;
-};
-
-static void display(const char *data, const char *pattern, int flag)
+static void display(const char *data, const char *pattern)
 {
-	if (flag) {
+	int flag = option_mask32 >> 1; /* shift out -0 bit */
+	if (flag & (flag-1)) {
+		/* more than one field to show: print "FIELD:" pfx */
 		int n = printf("%s:", pattern);
 		while (n++ < 16)
 			bb_putchar(' ');
@@ -45,55 +60,45 @@
 }
 
 static void modinfo(const char *path, const char *version,
-			const struct modinfo_env *env)
+			const char *field)
 {
-	static const char *const shortcuts[] = {
-		"filename",
-		"license",
-		"author",
-		"description",
-		"version",
-		"alias",
-		"srcversion",
-		"depends",
-		"uts_release",
-		"vermagic",
-		"parm",
-		"firmware",
-	};
 	size_t len;
 	int j;
 	char *ptr, *the_module;
-	const char *field = env->field;
-	int tags = env->tags;
+	char *allocated;
+	int tags = option_mask32;
 
-	if (tags & 1) { /* filename */
-		display(path, shortcuts[0], 1 != tags);
-	}
-
+	allocated = NULL;
 	len = MAXINT(ssize_t);
 	the_module = xmalloc_open_zipped_read_close(path, &len);
 	if (!the_module) {
 		if (path[0] == '/')
 			return;
 		/* Newer depmod puts relative paths in modules.dep */
-		path = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path);
+		path = allocated = xasprintf("%s/%s/%s", CONFIG_DEFAULT_MODULES_DIR, version, path);
 		the_module = xmalloc_open_zipped_read_close(path, &len);
-		free((char*)path);
-		if (!the_module)
-			return;
+		if (!the_module) {
+			bb_error_msg("module '%s' not found", path);
+			goto ret;
+		}
 	}
 
-	if (field)
-		tags |= OPT_F;
-	for (j = 1; (1<<j) & (OPT_TAGS + OPT_F); j++) {
+	for (j = 1; (1<<j) & (OPT_TAGS|OPT_F); j++) {
 		const char *pattern;
 
 		if (!((1<<j) & tags))
 			continue;
+
 		pattern = field;
 		if ((1<<j) & OPT_TAGS)
-			pattern = shortcuts[j];
+			pattern = shortcuts[j-2];
+
+		if (strcmp(pattern, shortcuts[0]) == 0) {
+			/* "-n" or "-F filename" */
+			display(path, shortcuts[0]);
+			continue;
+		}
+
 		ptr = the_module;
 		while (1) {
 			char *after_pattern;
@@ -106,7 +111,7 @@
 				/* field prefixes are 0x80 or 0x00 */
 				if ((ptr[-1] & 0x7F) == 0x00) {
 					ptr = after_pattern + 1;
-					display(ptr, pattern, (1<<j) != tags);
+					display(ptr, pattern);
 					ptr += strlen(ptr);
 				}
 			}
@@ -114,15 +119,18 @@
 		}
 	}
 	free(the_module);
+ ret:
+	free(allocated);
 }
 
 //usage:#define modinfo_trivial_usage
-//usage:       "[-adlp0] [-F keyword] MODULE"
+//usage:       "[-adlpn0] [-F keyword] MODULE"
 //usage:#define modinfo_full_usage "\n\n"
 //usage:       "	-a		Shortcut for '-F author'"
 //usage:     "\n	-d		Shortcut for '-F description'"
 //usage:     "\n	-l		Shortcut for '-F license'"
 //usage:     "\n	-p		Shortcut for '-F parm'"
+////usage:     "\n	-n		Shortcut for '-F filename'"
 //usage:     "\n	-F keyword	Keyword to look for"
 //usage:     "\n	-0		Separate output with NULs"
 //usage:#define modinfo_example_usage
@@ -131,7 +139,7 @@
 int modinfo_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 int modinfo_main(int argc UNUSED_PARAM, char **argv)
 {
-	struct modinfo_env env;
+	const char *field;
 	char name[MODULE_NAME_LEN];
 	struct utsname uts;
 	parser_t *parser;
@@ -139,10 +147,12 @@
 	unsigned opts;
 	unsigned i;
 
-	env.field = NULL;
+	field = NULL;
 	opt_complementary = "-1"; /* minimum one param */
-	opts = getopt32(argv, "nladvAsDumpF:0", &env.field);
-	env.tags = opts & OPT_TAGS ? opts & OPT_TAGS : OPT_TAGS;
+	opts = getopt32(argv, "0F:nadlp", &field);
+	/* If no field selected, show all */
+	if (!(opts & (OPT_TAGS|OPT_F)))
+		option_mask32 |= OPT_TAGS;
 	argv += optind;
 
 	uname(&uts);
@@ -159,7 +169,7 @@
 		filename2modname(bb_basename(tokens[0]), name);
 		for (i = 0; argv[i]; i++) {
 			if (fnmatch(argv[i], name, 0) == 0) {
-				modinfo(tokens[0], uts.release, &env);
+				modinfo(tokens[0], uts.release, field);
 				argv[i] = (char *) "";
 			}
 		}
@@ -169,7 +179,7 @@
 
 	for (i = 0; argv[i]; i++) {
 		if (argv[i][0]) {
-			modinfo(argv[i], uts.release, &env);
+			modinfo(argv[i], uts.release, field);
 		}
 	}