libkmod: dump index files

Provide a function to dump the index files to a certain fd. It could be
more optimized (particularly the functions to dump the index that were
copied and pasted from m-i-t), but it seems like the only user of it is
'modprobe -c', used for debugging purposes. So, keep it as is.
diff --git a/TODO b/TODO
index 2e6987b..d1d553a 100644
--- a/TODO
+++ b/TODO
@@ -11,18 +11,6 @@
 * create test-mock library to be LD_PRELOAD'ed before running the binaries
   so we're able to create unit tests
 
-* Add functions to dump configuration. Create a list with the config items
-  (blacklist, aliases, etc) or just dump to a fd?
-
-* Add functions to list all modules known by modules.dep
-
-* provide 1:1 compatibility with module-init-tools's modprobe
-   - dump modules.alias and modules.symbols
-
-* Add docs to kmod_config_* functions
-
-* Add manpages: copy them from module-init-tools and make the necessary changes
-
 * review API, maybe unify all of these setters:
    - kmod_module_version_get_symbol()
    - kmod_module_version_get_crc()
@@ -63,10 +51,14 @@
 ===================================================================
 
 * list of currently loaded modules
+	- readdir() in /sys/modules: dirs without a 'initstate' file mean the
+	  modules is builtin.
 
 * module's size should be available under /sys
+	- DONE in 3.3: http://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commit;h=cca3e707301862ca9b9327e6a732463982f8cd1b
 
 * kill /proc/modules ?
+	- Unlikely, given other tools might depend on it
 
 Things that are different from module-init-tools on purpose (!TODO)
 ===================================================================
diff --git a/libkmod/libkmod-index.c b/libkmod/libkmod-index.c
index 15e4898..9d3b939 100644
--- a/libkmod/libkmod-index.c
+++ b/libkmod/libkmod-index.c
@@ -211,6 +211,19 @@
 	return true;
 }
 
+static unsigned buf_pushchars(struct buffer *buf, const char *str)
+{
+	unsigned i = 0;
+	int ch;
+
+	while ((ch = str[i])) {
+		buf_pushchar(buf, ch);
+		i++;
+	}
+
+	return i;
+}
+
 static unsigned buf_freadchars(struct buffer *buf, FILE *in)
 {
 	unsigned i = 0;
@@ -383,6 +396,48 @@
 	return NULL;
 }
 
+static void index_dump_node(struct index_node_f *node, struct buffer *buf,
+								int fd)
+{
+	struct index_value *v;
+	int ch, pushed;
+
+	pushed = buf_pushchars(buf, node->prefix);
+
+	for (v = node->values; v != NULL; v = v->next) {
+		write_str_safe(fd, buf->bytes, buf->used);
+		write_str_safe(fd, " ", 1);
+		write_str_safe(fd, v->value, strlen(v->value));
+		write_str_safe(fd, "\n", 1);
+	}
+
+	for (ch = node->first; ch <= node->last; ch++) {
+		struct index_node_f *child = index_readchild(node, ch);
+
+		if (!child)
+			continue;
+
+		buf_pushchar(buf, ch);
+		index_dump_node(child, buf, fd);
+		buf_popchar(buf);
+	}
+
+	buf_popchars(buf, pushed);
+	index_close(node);
+}
+
+void index_dump(struct index_file *in, int fd, const char *prefix)
+{
+	struct index_node_f *root;
+	struct buffer buf;
+
+	buf_init(&buf);
+	buf_pushchars(&buf, prefix);
+	root = index_readroot(in);
+	index_dump_node(root, &buf, fd);
+	buf_release(&buf);
+}
+
 static char *index_search__node(struct index_node_f *node, const char *key, int i)
 {
 	char *value;
@@ -810,6 +865,50 @@
 	return NULL;
 }
 
+static void index_mm_dump_node(struct index_mm_node *node, struct buffer *buf,
+								int fd)
+{
+	struct index_mm_value *itr, *itr_end;
+	int ch, pushed;
+
+	pushed = buf_pushchars(buf, node->prefix);
+
+	itr = node->values.values;
+	itr_end = itr + node->values.len;
+	for (; itr < itr_end; itr++) {
+		write_str_safe(fd, buf->bytes, buf->used);
+		write_str_safe(fd, " ", 1);
+		write_str_safe(fd, itr->value, itr->len);
+		write_str_safe(fd, "\n", 1);
+	}
+
+	for (ch = node->first; ch <= node->last; ch++) {
+		struct index_mm_node *child = index_mm_readchild(node, ch);
+
+		if (child == NULL)
+			continue;
+
+		buf_pushchar(buf, ch);
+		index_mm_dump_node(child, buf, fd);
+		buf_popchar(buf);
+	}
+
+	buf_popchars(buf, pushed);
+	index_mm_free_node(node);
+}
+
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix)
+{
+	struct index_mm_node *root;
+	struct buffer buf;
+
+	buf_init(&buf);
+	buf_pushchars(&buf, prefix);
+	root = index_mm_readroot(idx);
+	index_mm_dump_node(root, &buf, fd);
+	buf_release(&buf);
+}
+
 static char *index_mm_search_node(struct index_mm_node *node, const char *key,
 									int i)
 {
diff --git a/libkmod/libkmod-index.h b/libkmod/libkmod-index.h
index e9cd456..0134ac5 100644
--- a/libkmod/libkmod-index.h
+++ b/libkmod/libkmod-index.h
@@ -114,6 +114,7 @@
 struct index_file *index_file_open(const char *filename);
 void index_file_close(struct index_file *idx);
 char *index_search(struct index_file *idx, const char *key);
+void index_dump(struct index_file *in, int fd, const char *prefix);
 struct index_value *index_searchwild(struct index_file *idx, const char *key);
 
 void index_values_free(struct index_value *values);
@@ -125,5 +126,6 @@
 void index_mm_close(struct index_mm *index);
 char *index_mm_search(struct index_mm *idx, const char *key);
 struct index_value *index_mm_searchwild(struct index_mm *idx, const char *key);
+void index_mm_dump(struct index_mm *idx, int fd, const char *prefix);
 
 #endif
diff --git a/libkmod/libkmod.c b/libkmod/libkmod.c
index 8696945..f544c66 100644
--- a/libkmod/libkmod.c
+++ b/libkmod/libkmod.c
@@ -746,6 +746,39 @@
 	}
 }
 
+KMOD_EXPORT int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index type,
+									int fd)
+{
+	if (ctx == NULL)
+		return -ENOSYS;
+
+	if (type < 0 || type >= _KMOD_INDEX_MODULES_SIZE)
+		return -ENOENT;
+
+	if (ctx->indexes[type] != NULL) {
+		DBG(ctx, "use mmaped index '%s'\n", index_files[type].fn);
+		index_mm_dump(ctx->indexes[type], fd,
+						index_files[type].prefix);
+	} else {
+		char fn[PATH_MAX];
+		struct index_file *idx;
+
+		snprintf(fn, sizeof(fn), "%s/%s.bin", ctx->dirname,
+						index_files[type].fn);
+
+		DBG(ctx, "file=%s\n", fn);
+
+		idx = index_file_open(fn);
+		if (idx == NULL)
+			return -ENOSYS;
+
+		index_dump(idx, fd, index_files[type].prefix);
+		index_file_close(idx);
+	}
+
+	return 0;
+}
+
 const struct kmod_list *kmod_get_blacklists(const struct kmod_ctx *ctx)
 {
 	return ctx->config->blacklists;
diff --git a/libkmod/libkmod.h b/libkmod/libkmod.h
index a3939d3..64fe064 100644
--- a/libkmod/libkmod.h
+++ b/libkmod/libkmod.h
@@ -69,6 +69,8 @@
 	_KMOD_INDEX_PAD = (1 << 31),
 };
 
+int kmod_dump_index(struct kmod_ctx *ctx, enum kmod_index idx, int fd);
+
 /*
  * kmod_list
  *
diff --git a/libkmod/libkmod.sym b/libkmod/libkmod.sym
index 500af4e..f2d704d 100644
--- a/libkmod/libkmod.sym
+++ b/libkmod/libkmod.sym
@@ -94,4 +94,5 @@
 	kmod_config_iter_get_value;
 	kmod_config_iter_next;
 	kmod_config_iter_free_iter;
+	kmod_dump_index;
 } LIBKMOD_3;