#include <stdio.h>
#include <assert.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "tool.h"
#include "link.h"
#include "kernelsyms.h"


/*
 *	Add the symbol defined in the kernel, simulating a pseudo module
 *	Return -1 if any error.
 */
static int depmod_addksyms (
	MODULES &mods,
	SYMBOLS &syms)
{
	int ret = -1;
	MODULE *mod = mods.setdummy ("-");
	SYMBOL *tbsym[2000];
	int nbsym=1;

	// Fake the _mod_use_count_ symbol which is provided by insmod
	int requis;		// Dummy
	tbsym[0] = syms.add ("mod_use_count_",mod,SYM_DEFINI,requis,0);
	struct kernel_sym *ksym;
	int so_far = 0;

	load_kernel_symbols();
	int kernel_syms = 0;
	/*
	 * Specification: depmod / kernel syms only
	 * When initialising its symbol table from the kernel
	 * depmod silently discards all symbol from loaded modules.
	 *
	 * This means that depmod may be used at any time to compute
	 * the dependancy table, even if there are modules already
	 * loaded.
	 *
	 * depmod use the kernel system call to obtain the
	 * symbol table, not /proc/ksyms. depmod assume that
	 * kernel symbols are at the end of the list, just
	 * after a pseudo symbol with a one character name: #
	 */
	int a_out_kernel = 0;
	for (ksym = ksymtab; so_far < nksyms ; ++so_far, ksym++) {
		if (kernel_syms) {
			if (ksym->name[0] == '#')
				continue;
			assert (nbsym < 2000);
			// If a_out kernel, skip leading '_'
			tbsym[nbsym++] = syms.add (ksym->name + a_out_kernel,
						mod,SYM_DEFINI,requis,0);
		}
		else if (strcmp(ksym->name,"#") == 0) {
			kernel_syms = 1;
			// This works... I promise!
			if ((ksym + 1)->name[0] == '_')
				a_out_kernel = 1;
		}
	}
	int size = nbsym*sizeof(SYMBOL*);
	mod->pub.tb = (SYMBOL**)malloc(size);
	if (mod->pub.tb == NULL){
		depmod_error ("Can't allocate memory for kernel symbols");
	}else{
		memcpy (mod->pub.tb,tbsym,size);
		ret = 0;
	}
	return ret;
}

/*
 *	Prints the dependancies in the output or stdout if depfile is NULL.
 */
static void depmod_prtdepend(
	MODULES &mods,
	const char *depfile,
	int verbose,
	int showerror)
{
	FILE *out = stdout;
	if (depfile != NULL){
		out = fopen (depfile,"w");
		if (out == NULL){
			depmod_error ("Can't open %s",depfile);
			exit (-1);
		}
	}								
	mods.prtdepend(out,"-",verbose,showerror);

}

int depmod_main (int argc, char *argv[])
{
	int ret = -1;
	int showerror = 0;
	int verbose = 0;
	int stdmode = 0;
	int err = 0;

	if (argc == 1) {
		fprintf (stderr,
			"depmod " DEPMOD_RELEASE "\n"
			"depmod [-d -e -v ] -a [forced_version]\n"
			"depmod [-d -e -v ] module1.o module2.o ...\n"
			"\n"
			"depmod will output a dependancy list suitable for the\n"
			"modprobe utility. This dependancy file look like\n"
			"a Makefile\n"
			"\n"
			"It reads the kernel symbol table to find out which\n"
			"symbols are already available in the running kernel.\n"
			"\n"
			"depmod -a will find the list of module to probe from\n"
			"the file " ETC_CONF_MODULES ". It will output the result\n"
			"into the depfile specified in this configuration file\n"
			"(depfile=...)\n"
			"\n"
			"Normally depmod operate silently, reporting only the list\n"
			"of module that won't load properly (missing symbols).\n"
			"Option -e output all the unresolved symbol for a given module\n"
			"Option -s output all error message to the syslog daemon\n"
			"Option -v force a printout of all visited modules.\n"
			);
		return -1;
	}
	/* else */

	MODULES mods;
	SYMBOLS syms;

	if (depmod_addksyms(mods, syms) == -1)
		return -1;
	/* else */

	for (++argv, --argc; argc > 0 && argv[0][0] == '-'; ++argv, --argc) {
		char *arg = argv[0];
		while (*++arg) {
			switch (*arg) {
			case 'a': stdmode = 1;	// Probe standard directory
				break;		// using the config file

			case 'd': debugmode = 1;
				break;

			case 'e': showerror = 1;
				break;

			case 's': depmod_setsyslog("depmod");
				break;

			case 'v': verbose = 1;
				break;

			case 'V': printf("depmod version %s\n", DEPMOD_RELEASE);
				break;

			default: err = 1;
				break;
			}
		}
	}

	if (err) {
		depmod_error ("Aborting");
		return -1;
	}
	/* else */

	if (stdmode) {
		if (argc > 0) {
			if (config_read(*argv) == -1) {
		    		depmod_error ("%s does not exist",
					config_getdepfile());
				return -1;
			}
		}
		else if (config_read(NULL) == -1)
			return -1;

		char *lst[1000];
		int i;
		int nb = config_lstmod ("*",NULL,lst,1);
		for (i = 0, ret = 0; i < nb && ret != -1; i++) {
			mods.loadobj(syms, lst[i]);
		}
		if (ret != -1) {
			depmod_prtdepend (mods, config_getdepfile()
				,verbose, showerror);
		}
	}
	else { // not stdmode
		for (ret = 0; argc > 0 && ret != -1; ++argv, --argc)
			mods.loadobj (syms, *argv);
		if (ret != -1)
			depmod_prtdepend(mods,NULL,verbose,showerror);
	}

	return ret;			
}
