Index: drush_mm/drush_mm.inc
===================================================================
RCS file: drush_mm/drush_mm.inc
diff -N drush_mm/drush_mm.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.inc	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,297 @@
+<?php
+
+/**
+ * Get all folder from the given directory
+ * 
+ * from php.net
+ * - getRecursiveFolderList
+ */
+function _drush_mm_get_recursive_folder_list($curDir, $currentA=false) {                   
+//  echo "scanning $curDir \n";
+  //TODO: file_scan_directory @ includes/file.inc
+  //TODO: using DIR_ONLY misses symlinks
+  $dirs = glob($curDir . '/*');    
+  $cur = 0;
+  foreach($dirs as $dir) {
+    $currentA[$cur]['path'] = $dir;
+    $currentA[$cur] = _drush_mm_get_recursive_folder_list($dir, $currentA[$cur]);
+    ++$cur;
+  }
+  return $currentA;
+}
+
+/**
+ * Callback to collect info from each *.info file found
+ * 
+ * Each info file is parsed and stored into a module_info array
+ * Some fields are used for calculating the dependencies graph
+ * Other fields are nice for generating the dot output.
+ */
+function _drush_mm_get_module_info($item, $key){
+  global $_drush_mm_module_info;
+  
+  foreach( glob( $item .'/*.info') as $file){
+    $base_name= basename( $file, '.info');
+    
+    // check later whether module is encountered more then once
+    $module_info[ $base_name ]['count']++;
+
+    // nice to know to recalculate the original package
+    $module_info[ $base_name ]['dir']= dirname( $file);
+
+    $_drush_mm_module_info[ $base_name]= _module_parse_info_file( $file);
+    $_drush_mm_module_info[ $base_name]['dependencies']= $_drush_mm_module_info[ $base_name]['dependencies'];
+  }
+}
+
+function _drush_mm_list_modules( $modules_info){
+  $list = array();
+  
+  foreach( $modules_info as $key=>$value){
+    if( $value['name']){
+    	// TODO: omit the version when not verbose (better for scripting the list)
+    	if(  DRUSH_VERBOSE){
+        $list[ $value['status']][]=  $key .' ('. $value['version'] .')';
+    	}
+    	else {
+        $list[ $value['status']][]=  $key;
+    	}
+    } else {
+      if( isset($value['status'])){
+        $list['error'][]= $key;
+      }
+      else {
+        $list['missing'][]= $key;
+      }
+    }
+  }
+  
+  sort( $list['error']);
+  sort( $list['missing']);
+  sort( $list[0]);
+  sort( $list[1]);
+  
+  if( count($list['error'])){
+    echo "According to the system table available modules but not installed!\n";
+    echo "  ERROR: " . implode( ", ", $list['error']) . "\n\n";
+  }
+
+  if( count($list['missing'])){
+    echo "missing: " . implode( " ", $list['missing']) . "\n\n";
+  }
+  echo "enabled: " . implode( " ", $list[1]) . "\n\n";
+  echo "disabled: " . implode( " ", $list[0]) . "\n\n";
+  
+}
+
+function _drush_mm_generate_dot( $modules_info){
+  // print_r( $modules_info);
+  echo "digraph {\n";
+  
+  // make this a two pass
+  // 1. nodes
+  // 1.1 use subgraphs for same package based modules
+  // 1.2 use colors 
+  // 2. connections
+  foreach( $modules_info as $key=>$value){
+    echo '  "'.$key .'" [ style=filled, color='
+      . ( isset( $value['name']) ? ($value['status'] ? 'green' : 'yellow') : 'red')
+      ." ];";
+      
+  	if( isset( $value['dependencies'])){
+      foreach( $value['dependencies'] as $dep){
+        echo "  \"$key\" -> \"$dep\" \n";
+      }
+    } else {
+      echo "  \"$key\" \n";
+    }
+  }
+  echo "}";
+}
+
+function _drush_mm_get_modules_info(){
+  // storage for all module info
+  global $_drush_mm_module_info;
+  $_drush_mm_module_info= array();
+
+  foreach( $paths= _drush_mm_get_module_paths() as $path){
+    $tree = _drush_mm_get_recursive_folder_list( $path);
+    array_walk_recursive( $tree, '_drush_mm_get_module_info');
+  }
+  
+  // TODO: add the system settings
+  $result= db_query("SELECT name, status FROM {system} WHERE type = 'module'");
+  while( $module= db_fetch_object($result)){
+  	$_drush_mm_module_info[$module->name]['status']= $module->status;
+  }
+  
+  // fill in the rdependencies
+  foreach( $_drush_mm_module_info as $key=>$module){
+    foreach( $module['dependencies'] as $depend){
+      // registrer the reverse dependencies
+      $_drush_mm_module_info[$depend]['rdependencies'][]= $key;
+    }
+  }
+}
+
+/**
+ * based on the modules at hand the dependencies list is generated
+ * 
+ */
+function _drush_mm_get_module_list( $modules, $enable){
+  global $_drush_mm_module_info;
+  _drush_mm_get_modules_info();
+
+  // Generate the list
+  $modules_to_use = array();
+
+  foreach( $modules as $module){
+  	$modules_to_use[$module]= $_drush_mm_module_info[$module];
+  }
+  
+  $dependency_key = $enable ? 'dependencies' : 'rdependencies';
+  
+  // add dependencies
+  $more= TRUE;
+  while( $more){
+    $more= FALSE;
+    foreach( $modules_to_use as $key=>$module){
+      // add all (r)dependencies to the list
+      foreach( $module[ $dependency_key] as $depend){
+        if( !array_key_exists( $depend, $modules_to_use)){
+          $modules_to_use[$depend]= $_drush_mm_module_info[$depend];
+		  $more= TRUE;
+        }
+      }
+    }
+  }  
+
+  // module names added by a rdepend may not exists
+  $missing_modules= array();
+  foreach( $modules_to_use as $module => $values){
+  	if( !isset( $values['name'])){
+  		$missing_modules[] = $module;
+  	}
+  }
+  if( count( $missing_modules)){
+  	$did_you_mean= array();
+  	$module_names = array_keys( $_drush_mm_module_info);
+    drush_verbose( 'missing modules found ... building suggestions');
+  	foreach ( $missing_modules as $missing_module){
+      drush_verbose( "testing $missing_module ..", 1);
+  		foreach( $module_names as $existing_module){
+        $match = similar_text($missing_module, $existing_module);
+  			drush_verbose( "similarity to $existing_module .. $match", 2);
+  			if($match> strlen($missing_module)-2){
+  			 $did_you_mean[$missing_module].= " " . $existing_module;
+  		  }
+  		}
+  	}
+    if( count($did_you_mean)){
+    	$suggestion = t('did you mean: ') . implode("\n", $did_you_mean);
+    }
+    else {
+    	$suggestion = t('no suggestions found!');
+    }
+  	drush_die(t("The following modules where not found. '!modules'\n  !suggestion" ,
+  	 array( "!suggestion" => $suggestion, "!modules"=> implode( ", ", $missing_modules))));
+  }  
+  
+  $graph= _drush_mm_build_graph( $modules_to_use, $dependency_key);
+  
+  $result= _drush_mm_graph_tsl( $graph);
+  
+  if( array_shift($result) != "_drupal_"){
+    drush_die(t("Unexpected result for '!modules'." , array( "!modules"=> implode( ", ", $result))));
+  }
+  if( array_pop($result) != "_root_"){
+    drush_die(t("Unexpected result for '!modules'." , array( "!modules"=> implode( ", ", $result))));
+  }
+  
+  return $result;
+
+}
+
+function _drush_mm_build_graph( $modules_to_use, $dependency_key){
+  // build graph
+  $graph= array();
+  foreach($modules_to_use as $key => $module){
+    // make undependants dependant
+    if( count($module[ $dependency_key])){
+      foreach( $module[$dependency_key] as $depend){
+        $graph[$key][$depend]++;
+      }
+    } else {
+      // we make the independant 'drupal' dependent
+      $graph[$key]['_drupal_']++;
+    }
+  }
+
+  // make all rdepend-less rdepend to _root_
+  foreach( $graph as $key=>$node){
+  	if( !count( $modules_to_use[$key]['rdepend'])){
+  	  $graph['_root_'][$key]++;
+  	}
+  }
+  return $graph;
+}
+
+function _drush_mm_get_module_paths(){
+  $paths = array();
+  
+  if (DRUSH_URI) {
+    $path = conf_path();
+    $paths['site'] = DRUSH_DRUPAL_ROOT .'/'. $path .'/modules/';
+  }
+  
+  if (file_exists(DRUSH_DRUPAL_ROOT .'/sites/all/modules/')) {
+    $paths['all'] = DRUSH_DRUPAL_ROOT .'/sites/all/modules/';
+  }
+  
+  $paths['core'] = DRUSH_DRUPAL_ROOT .'/modules/';
+  
+  return $paths;
+}
+
+/**
+ * Generares a topological sorted list
+ * 
+ * @param $graph
+ *   the graph to get a TSL from
+ */
+function _drush_mm_graph_tsl( $graph){
+  $result= array();
+  
+  $reset = TRUE;
+  foreach( $graph as $key=>$value){
+    $result= _drush_mm_graph_df($graph, $key, $reset);
+    $reset= FALSE;
+  }
+
+  return $result;
+}
+
+/**
+ * do a depth first search
+ * 
+ * Each node will be visited. The more depth first.
+ */
+function _drush_mm_graph_df( $graph, $node, $reset=false){
+  static $visited;
+  static $result;
+  
+  if( !isset( $visited) || $reset){
+    $visited= array();
+    $result= array();
+  }
+  
+  if( !isset($visited[$node])){
+    $visited[$node]=TRUE;
+    foreach( $graph[$node] as $key=> $_node){
+      _drush_mm_graph_df( $graph, $key);
+    }
+    array_push($result, $node);
+  }
+  
+  return $result;
+}
Index: drush_mm/drush_mm.info
===================================================================
RCS file: drush_mm/drush_mm.info
diff -N drush_mm/drush_mm.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.info	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,5 @@
+; $Id$
+name = Drush Module Manager
+description = Allows you to enable/disable modules and generated a graph from the command line.
+dependencies = drush
+package = drush
Index: drush_mm/README.txt
===================================================================
RCS file: drush_mm/README.txt
diff -N drush_mm/README.txt
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/README.txt	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,23 @@
+// $Id$
+
+DESCRIPTION
+-----------
+
+drush_mm.module (The Drupal Shell Module Manager) allows you to enable and disable modules from the command line.
+
+It provides three commands
+- enable  : enabled the given module and all modules it depends on
+- disable : disable the given module and all modules depending on
+- dot     : generated a dot file for further processing into a webpage
+
+Run these as "drush mm <command>"
+
+Run "drush help mm <command>" to see supported command line options and arguments.
+
+REQUIREMENTS
+------------
+none
+
+------------
+Written by Clemens Tolboom (ngnp) <http://b>.
+No warranties of any kind. Use with care.
Index: drush_mm/drush_mm.module
===================================================================
RCS file: drush_mm/drush_mm.module
diff -N drush_mm/drush_mm.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ drush_mm/drush_mm.module	1 Jan 1970 00:00:00 -0000
@@ -0,0 +1,153 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ *   The drush Module Manager
+ */
+
+/**
+ * Implementation of hook_help().
+ */
+function drush_mm_help($section) {
+	switch ($section) {
+		case 'drush:mm list':
+			return t("Usage: drush [options] mm list ... lists a computer readable name of the installed modules.\n\n");
+		case 'drush:mm enable':
+			return t("Usage: drush [options] mm enable <module_1> <module_2> ... <module_n> is the computer readable name of one of the installed modules.\nAfter enabling, you still have to configure the modules through the modules administration page\n\n");
+		case 'drush:mm disable':
+			return t("Warning: do not disable required core modules!!!\n\nUsage: drush [options] mm disable <module_1> <module_2> ... <module_n> is the computer readable name of one of the modules to install.\n\n");
+		case 'drush:mm uninstall':
+			return t("Warning: do not uninstall required core modules!!!\n\nUsage: drush [options] mm uninstall <module_1> <module_2> ... <module_n> is the computer readable name of one of the modules to uninstall.\n\n");
+		case 'drush:mm dot':
+			return t("Usage: drush [options] mm dot ... generated a graphviz dot file for further processing.\n\n");
+	}
+}
+
+/**
+ * Implementation of hook_drush_command().
+ */
+function drush_mm_drush_command() {
+	$items['mm list'] = array(
+    'callback' => 'drush_mm_list_modules',
+    'description' => 'Lists the available modules'
+  );
+
+  $items['mm enable'] = array(
+    'callback' => 'drush_mm_enable_module',
+    'description' => 'Enables (and if necessary installs) the given modules and depend'
+  );
+
+  $items['mm disable'] = array(
+    'callback' => 'drush_mm_disable_module',
+    'description' => 'Disable the given modules and reverse dependants'
+  );
+
+  $items['mm uninstall'] = array(
+    'callback' => 'drush_mm_uninstall_module',
+    'description' => 'Uninstalls the given modules and reverse dependants'
+  );
+
+  $items['mm dot'] = array(
+    'callback' => 'drush_mm_dot',
+    'description' => 'Generates a dot file of all modules'
+  );
+  
+  return $items;
+}
+
+function drush_mm_list_modules() {
+	require_once "drush_mm.inc";
+	global $_drush_mm_module_info;
+	_drush_mm_get_modules_info();
+
+	_drush_mm_list_modules($_drush_mm_module_info);
+}
+
+function drush_mm_dot() {
+	require_once "drush_mm.inc";
+	global $_drush_mm_module_info;
+	_drush_mm_get_modules_info();
+
+	_drush_mm_generate_dot($_drush_mm_module_info);
+}
+
+/**
+ * enables the given modules
+ *
+ * If all modules are present the are enabled
+ */
+function drush_mm_enable_module() {
+	$modules = func_get_args();
+	_drush_mm_endis_module( TRUE, $modules);
+}
+
+function drush_mm_uninstall_module(){
+	$modules = func_get_args();
+
+	if( empty($modules)){
+		drush_die(t("No modules specified.\n\nRun drush help mm uninstall for more information."));
+	}
+
+	drush_verbose( t("First disabling modules ..."));
+	call_user_func_array( 'drush_mm_disable_module', $modules);
+
+	drush_verbose( t("Now uninstalling modules ..."));
+
+	$enable= FALSE;
+	require_once "drush_mm.inc";
+	$result= _drush_mm_get_module_list( $modules, $enable);
+
+	foreach( $result as $module){
+		if( !DRUSH_SIMULATE){
+			include_once './includes/install.inc';
+			drush_verbose( t("Uninstalling module $module"));
+			drupal_uninstall_module( $module);
+		}
+	}
+}
+
+function _drush_mm_endis_module( $enable, $modules){
+	if (empty($modules)) {
+		if( $enable){
+			drush_die(t("No modules specified.\n\nRun drush help mm enable for more information."));
+		} else {
+			drush_die(t("No modules specified.\n\nRun drush help mm disable for more information."));
+		}
+	}
+
+	drush_verbose(t("Given module names: '!modules'" , array( "!modules"=> implode( ", ", $modules))));
+
+	require_once "drush_mm.inc";
+	$result= _drush_mm_get_module_list( $modules, $enable);
+
+	if( $enable){
+		drush_verbose(t("The following modules will be enabled: '!modules'" , array( "!modules"=> implode( ", ", $result))));
+		//module_enable( $result);
+		include_once './includes/install.inc';
+		drush_verbose( t("Installing modules"));
+		foreach( $result as $module){
+			drush_verbose( t("Enabling (and mayby installing) module: $module"));
+			if( !DRUSH_SIMULATE){
+				// only FRESH installed modules gets enabled
+				drupal_install_modules( array($module));
+				// enable for sure
+				// TODO: this will not solve the 'failed install' scenario
+				module_enable( array($module));
+			}
+		}
+	} else {
+		drush_verbose(t("The following modules will be disabled: '!modules'" , array( "!modules"=> implode( ", ", $result))));
+		foreach( $result as $module){
+			drush_verbose( t("Disabling module: $module"));
+			if( !DRUSH_SIMULATE){
+				module_disable( $result);
+			}
+		}
+	}
+}
+
+function drush_mm_disable_module() {
+	$modules = func_get_args();
+	_drush_mm_endis_module( FALSE, $modules);
+}
