modprobe-small: optimizations for single applet build

(I'm requesting for a review first because I fear such an aggressive
change could lead to bugs. While I observe the sizes have reduced, I
haven't test the functionality of each applet after that. So please
test before merging.)

Aggressively cut off unneeded code when the relevant applets are not
built.

Correct dependencies of FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE and
FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED.

Don't bother with the '-r' option check if only rmmod is built (assume
true then), or when neither rmmod or mobprobe is built (assume false
then).

Size comparison before and after the change (single applet
configuration):

     text    data     bss     dec     hex filename
    34778     946     112   35836    8bfc old/busybox_DEPMOD
    34151     946     112   35209    8989 new/busybox_DEPMOD

    34903     946     112   35961    8c79 old/busybox_INSMOD
    28316     778     112   29206    7216 new/busybox_INSMOD

    35228     962     112   36302    8dce old/busybox_LSMOD
     5011     706      40    5757    167d new/busybox_LSMOD

    34830     946     112   35888    8c30 old/busybox_MODPROBE
    34795     946     112   35853    8c0d new/busybox_MODPROBE

    34718     946     112   35776    8bc0 old/busybox_RMMOD
     7502     714     104    8320    2080 new/busybox_RMMOD

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
diff --git a/modutils/modprobe-small.c b/modutils/modprobe-small.c
index 0fc9ea4..12e0993 100644
--- a/modutils/modprobe-small.c
+++ b/modutils/modprobe-small.c
@@ -13,15 +13,14 @@
 //config:config FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
 //config:	bool "Accept module options on modprobe command line"
 //config:	default y
-//config:	depends on MODPROBE_SMALL
-//config:	select PLATFORM_LINUX
+//config:	depends on MODPROBE_SMALL && (INSMOD || MODPROBE)
 //config:	help
 //config:	  Allow insmod and modprobe take module options from command line.
 //config:
 //config:config FEATURE_MODPROBE_SMALL_CHECK_ALREADY_LOADED
 //config:	bool "Skip loading of already loaded modules"
 //config:	default y
-//config:	depends on MODPROBE_SMALL
+//config:	depends on MODPROBE_SMALL && (DEPMOD || INSMOD || MODPROBE)
 //config:	help
 //config:	  Check if the module is already loaded.
 
@@ -59,6 +58,14 @@
 
 #define DEPFILE_BB CONFIG_DEFAULT_DEPMOD_FILE".bb"
 
+#define ONLY_APPLET (ENABLE_MODPROBE + ENABLE_DEPMOD + ENABLE_INSMOD \
+  + ENABLE_LSMOD + ENABLE_RMMOD <= 1)
+#define is_modprobe (ENABLE_MODPROBE && (ONLY_APPLET || applet_name[0] == 'm'))
+#define is_depmod   (ENABLE_DEPMOD   && (ONLY_APPLET || applet_name[0] == 'd'))
+#define is_insmod   (ENABLE_INSMOD   && (ONLY_APPLET || applet_name[0] == 'i'))
+#define is_lsmod    (ENABLE_LSMOD    && (ONLY_APPLET || applet_name[0] == 'l'))
+#define is_rmmod    (ENABLE_RMMOD    && (ONLY_APPLET || applet_name[0] == 'r'))
+
 enum {
 	OPT_q = (1 << 0), /* be quiet */
 	OPT_r = (1 << 1), /* module removal instead of loading */
@@ -361,6 +368,8 @@
 {
 	int cur;
 	const char *fname;
+	bool is_remove = (ENABLE_RMMOD && ONLY_APPLET)
+		|| ((ENABLE_RMMOD || ENABLE_MODPROBE) && (option_mask32 & OPT_r));
 
 	pathname += 2; /* skip "./" */
 	fname = bb_get_last_path_component_nostrip(pathname);
@@ -385,7 +394,7 @@
 	if (parse_module(&modinfo[cur], pathname) != 0)
 		return TRUE; /* failed to open/read it, no point in trying loading */
 
-	if (!(option_mask32 & OPT_r)) {
+	if (!is_remove) {
 		if (load_module(pathname, module_load_options) == 0) {
 			/* Load was successful, there is nothing else to do.
 			 * This can happen ONLY for "top-level" module load,
@@ -666,7 +675,8 @@
 	module_info **infovec;
 	module_info *info;
 	int infoidx;
-	int is_remove = (option_mask32 & OPT_r) != 0;
+	bool is_remove = (ENABLE_RMMOD && ONLY_APPLET)
+		|| ((ENABLE_RMMOD || ENABLE_MODPROBE) && (option_mask32 & OPT_r));
 	int exitcode = EXIT_SUCCESS;
 
 	dbg1_error_msg("process_module('%s','%s')", name, cmdline_options);
@@ -675,9 +685,8 @@
 
 	dbg1_error_msg("already_loaded:%d is_remove:%d", already_loaded(name), is_remove);
 
-	if (applet_name[0] == 'r') {
-		/* rmmod.
-		 * Does not remove dependencies, no need to scan, just remove.
+	if (is_rmmod) {
+		/* Does not remove dependencies, no need to scan, just remove.
 		 * (compat note: this allows and strips .ko suffix)
 		 */
 		rmmod(name);
@@ -743,7 +752,7 @@
 
 	if (!infovec) {
 		/* both dirscan and find_alias found nothing */
-		if (!is_remove && applet_name[0] != 'd') /* it wasn't rmmod or depmod */
+		if (!is_remove && !is_depmod) /* it wasn't rmmod or depmod */
 			bb_error_msg("module '%s' not found", name);
 //TODO: _and_die()? or should we continue (un)loading modules listed on cmdline?
 		goto ret;
@@ -926,11 +935,10 @@
 {
 	int exitcode;
 	struct utsname uts;
-	char applet0 = applet_name[0];
-	IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options;)
+	IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(char *options = NULL;)
 
 	/* are we lsmod? -> just dump /proc/modules */
-	if (ENABLE_LSMOD && 'l' == applet0) {
+	if (is_lsmod) {
 		xprint_and_close_file(xfopen_for_read("/proc/modules"));
 		return EXIT_SUCCESS;
 	}
@@ -940,14 +948,13 @@
 	/* Prevent ugly corner cases with no modules at all */
 	modinfo = xzalloc(sizeof(modinfo[0]));
 
-	if (!ENABLE_INSMOD || 'i' != applet0) { /* not insmod */
+	if (!is_insmod) {
 		/* Goto modules directory */
 		xchdir(CONFIG_DEFAULT_MODULES_DIR);
 	}
 	uname(&uts); /* never fails */
 
-	/* depmod? */
-	if (ENABLE_DEPMOD && 'd' == applet0) {
+	if (is_depmod) {
 		/* Supported:
 		 * -n: print result to stdout
 		 * -a: process all modules (default)
@@ -978,28 +985,28 @@
 		return !wrote_dep_bb_ok;
 	}
 
-	/* insmod, modprobe, rmmod require at least one argument */
+#if ENABLE_MODPROBE || ENABLE_INSMOD || ENABLE_RMMOD
+	/* modprobe, insmod, rmmod require at least one argument */
 	opt_complementary = "-1";
 	/* only -q (quiet) and -r (rmmod),
 	 * the rest are accepted and ignored (compat) */
 	getopt32(argv, "qrfsvwb");
 	argv += optind;
 
-	/* are we rmmod? -> simulate modprobe -r */
-	if (ENABLE_RMMOD && 'r' == applet0) {
-		option_mask32 |= OPT_r;
-	}
-
-	if (!ENABLE_INSMOD || 'i' != applet0) { /* not insmod */
+	if (!is_insmod) {
 		/* Goto $VERSION directory */
 		xchdir(uts.release);
 	}
 
-#if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
+	/* are we rmmod? -> simulate modprobe -r, but don't bother the flag if
+	 * there're no other applets here */
+	if (is_rmmod) {
+		if (!ONLY_APPLET)
+			option_mask32 |= OPT_r;
+	} else if (!ENABLE_MODPROBE || !(option_mask32 & OPT_r)) {
+# if ENABLE_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE
 	/* If not rmmod/-r, parse possible module options given on command line.
 	 * insmod/modprobe takes one module name, the rest are parameters. */
-	options = NULL;
-	if (!(option_mask32 & OPT_r)) {
 		char **arg = argv;
 		while (*++arg) {
 			/* Enclose options in quotes */
@@ -1008,13 +1015,12 @@
 			free(s);
 			*arg = NULL;
 		}
-	}
-#else
-	if (!(option_mask32 & OPT_r))
+# else
 		argv[1] = NULL;
-#endif
+# endif
+	}
 
-	if (ENABLE_INSMOD && 'i' == applet0) { /* insmod */
+	if (is_insmod) {
 		size_t len;
 		void *map;
 
@@ -1023,8 +1029,7 @@
 		if (!map)
 			bb_perror_msg_and_die("can't read '%s'", *argv);
 		if (init_module(map, len,
-			IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : "")
-			IF_NOT_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE("")
+			(IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(options ? options : ) "")
 			) != 0
 		) {
 			bb_error_msg_and_die("can't insert '%s': %s",
@@ -1034,7 +1039,7 @@
 	}
 
 	/* Try to load modprobe.dep.bb */
-	if (!ENABLE_RMMOD || 'r' != applet0) { /* not rmmod */
+	if (!is_rmmod) {
 		load_dep_bb();
 	}
 
@@ -1049,4 +1054,5 @@
 		IF_FEATURE_MODPROBE_SMALL_OPTIONS_ON_CMDLINE(free(options);)
 	}
 	return exitcode;
+#endif /* MODPROBE || INSMOD || RMMOD */
 }