/*
 * sysdb.c  -  System database routines for mknbi-linux
 *
 * Copyright (C) 2005-2007 Gero Kuhlmann   <gero@gkminix.han.de>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 * $Id: sysdb.c,v 1.8 2007/01/06 18:31:26 gkminix Exp $
 */

#include <common.h>
#include <nblib.h>
#include "mknbi.h"

#ifndef MKNBI_H_LINUX
#error Included wrong header file
#endif


static struct sysdef dbdata;		/* Buffer for database data */




/*
 **************************************************************************
 *
 *		Process ramdisk mode string
 *
 **************************************************************************
 */

/*
 * Convert a string into a ramdisk load mode
 */
char *getrdmode __F((rdmodestr, sysentry),
				const char *rdmodestr AND
				struct sysdef *sysentry)
{
  char *cp;

  if (rdmodestr == NULL || !strcmp("auto", rdmodestr)) {
	sysentry->rdmode = RD_AUTO;
	sysentry->rdlocation = 0L;
  } else if (!strcmp("eom", rdmodestr)) {
	sysentry->rdmode = RD_EOM;
	sysentry->rdlocation = 0L;
  } else {
	sysentry->rdmode = RD_FIXED;
	if ((sysentry->rdlocation = strtol(rdmodestr, &cp, 0)) < 0x100000L)
		return("ramdisk image load location must be above 1M");
	else if (*cp)
		return("invalid ramdisk location");
  }
  return(NULL);
}



/*
 * Convert the ramdisk mode into a string
 */
static char *writerdmode __F((val, sysentry),
				char **val AND
				const struct sysdef *sysentry)
{
  char *cp = NULL;

  if (sysentry->rdmode == RD_AUTO)
	copystr(&cp, "auto");
  else if (sysentry->rdmode == RD_EOM)
	copystr(&cp, "eom");
  else {
	if (sysentry->rdlocation < 0x100000L)
		copystr(&cp, "auto");
	else {
		cp = (char *)nbmalloc(32);
		sprintf(cp, "0x%lx", sysentry->rdlocation);
	}
  }
  *val = cp;
  return(NULL);
}



/*
 * Process ramdisk mode string in database
 */
static char *procrdmode __F((name, arg, dowrite),
				char *name AND
				char **arg AND
				int dowrite)
{
  /* Check if we have to write the ramdisk mode into a string */
  if (dowrite)
	return(writerdmode(arg, &dbdata));

  /* Check if we have something to read at all */
  if (*arg == NULL)
	return(NULL);

  /* Otherwise convert the argument string */
  return(getrdmode(*arg, &dbdata));
}




/*
 **************************************************************************
 *
 *		Process VGA mode string
 *
 **************************************************************************
 */

/*
 * VGA mode strings
 */
static const struct {
	char *modename;
	int   modenum;
} vgamodes[] = {
	{ "default",	VGA_DEFAULT },
	{ "normal",	VGA_NORMAL },
	{ "extended",	VGA_EXTENDED },
	{ "ask",	VGA_ASK },
	{ NULL,		VGA_DEFAULT }
};



/*
 * Convert a string into a VGA kernel parameter
 */
char *getvgamode __F((vgamodestr, sysentry),
				const char *vgamodestr AND
				struct sysdef *sysentry)
{
  char *cp;
  long l;
  int i, mode = VGA_DEFAULT;

  if (vgamodestr != NULL) {
	for (i = 0; vgamodes[i].modename != NULL; i++) {
		if (!strcmp(vgamodes[i].modename, vgamodestr))
			break;
	}
	if (vgamodes[i].modename != NULL)
		mode = vgamodes[i].modenum;
	else {
		l = strtol(vgamodestr, &cp, 0);
		if (l < VGA_MIN || l > VGA_MAX || *cp)
			return("invalid VGA mode");
		mode = (int)l;
	}
  }
  sysentry->vgamode = mode;
  return(NULL);
}



/*
 * Convert the VGA mode into a string
 */
static char *writevgamode __F((val, sysentry),
				char **val AND
				const struct sysdef *sysentry)
{
  char *cp = NULL;
  int i;

  /* Find VGA mode in list of mode names */
  for (i = 0; vgamodes[i].modename != NULL; i++) {
	if (vgamodes[i].modenum == sysentry->vgamode) {
		copystr(&cp, vgamodes[i].modename);
		break;
	}
  }

  /* If not found, convert vga mode number into a string */
  if (cp == NULL) {
	if (sysentry->vgamode < VGA_MIN || sysentry->vgamode > VGA_MAX)
		copystr(&cp, "default");
	else {
		cp = (char *)nbmalloc(10);
		sprintf(cp, "%d", sysentry->vgamode);
	}
  }
  *val = cp;
  return(NULL);
}



/*
 * Process VGA mode string in database
 */
static char *procvgamode __F((name, arg, dowrite),
				char *name AND
				char **arg AND
				int dowrite)
{
  /* Check if we have to write the VGA mode into a string */
  if (dowrite)
	return(writevgamode(arg, &dbdata));

  /* Check if we have something to read at all */
  if (*arg == NULL)
	return(NULL);

  /* Otherwise convert the argument string */
  return(getvgamode(*arg, &dbdata));
}




/*
 **************************************************************************
 *
 *		Read a systems database entry
 *
 **************************************************************************
 */

/*
 * Parameters in each section of database file
 */
static struct paramdef dbparams[] = {
  { "outfile",	 	par_file,	NULL,	{&dbdata.outname}},
  { "kernel",		par_file,	NULL,	{&dbdata.kname}},
  { "ramdisk-image", 	par_file,	NULL,	{&dbdata.rdname}},
  { "ramdisk-mode",	par_proc,	NULL,	{(voidstar)&procrdmode}},
  { "vga-mode",		par_proc,	NULL,	{(voidstar)&procvgamode}},
  { "append",		par_string,	NULL,	{&dbdata.append}},
  { "root-dir",		par_string,	NULL,	{&dbdata.nfsdir}},
  { "ip-addrs",		par_string,	NULL,	{&dbdata.addrs}},
  { NULL,	 	par_null,	NULL,	{NULL}}
};



/*
 * Copy a string from the database entry into the systems entry
 */
static inline void copyentry __F((sysentry, dbentry),
				char **sysentry AND
				char **dbentry)
{
  if (*dbentry != NULL) {
	if (*sysentry == NULL && **dbentry != '\0')
		*sysentry = *dbentry;
	else
		free(*dbentry);
	*dbentry = NULL;
  }
}



/*
 * Read system database
 */
void getdb __F((name, sysentry), const char *name AND struct sysdef *sysentry)
{
  char *namebuf;
  int i;

  /* Read one entry from database file */
  namebuf = (char *)nbmalloc(strlen(name) + strlen(DBSECTNAME) + 2);
  sprintf(namebuf, "%s:%s", DBSECTNAME, name);
  memzero(&dbdata, sizeof(dbdata));
  dbdata.vgamode = VGA_NONE;
  dbdata.rdmode = RD_NONE;
  if (!opensysdb(TRUE))
	nbexit(-1);
  if (!readsysdb(namebuf, dbparams))
	nbexit(-1);
  closesysdb();

  /* Check that parameters are correct */
  if (dbdata.nfsdir != NULL &&
      (!strncmp(dbdata.nfsdir, "ram", 3) || !strcmp(dbdata.nfsdir, "initrd"))) {
	if (dbdata.rdname == NULL) {
		prnerr("need ramdisk image name in section <%s>", namebuf);
		nbexit(EXIT_DB);
	}
	for (i = 3; i < 6 && dbdata.nfsdir[i] != '\0'; i++)
		if (!isdigit((int)(dbdata.nfsdir[i])))
			break;
	if (dbdata.nfsdir[i] != '\0') {
		prnerr("invalid ramdisk device name in section <%s>", namebuf);
		nbexit(EXIT_DB);
	}
  }

  /* Copy database data into systems entry */
  copyentry(&(sysentry->outname), &(dbdata.outname));
  copyentry(&(sysentry->rdname), &(dbdata.rdname));
  copyentry(&(sysentry->nfsdir), &(dbdata.nfsdir));
  copyentry(&(sysentry->kname), &(dbdata.kname));
  copyentry(&(sysentry->append), &(dbdata.append));
  copyentry(&(sysentry->addrs), &(dbdata.addrs));
  if (dbdata.vgamode != VGA_NONE && sysentry->vgamode == VGA_NONE)
	sysentry->vgamode = dbdata.vgamode;
  if (dbdata.rdmode != RD_NONE && sysentry->rdmode == RD_NONE) {
	sysentry->rdmode = dbdata.rdmode;
	if (dbdata.rdmode == RD_FIXED)
		sysentry->rdlocation = dbdata.rdlocation;
	else
		sysentry->rdlocation = 0L;
  }

  /* Check for missing parameters */
  if (sysentry->kname == NULL) {
	prnerr("need kernel image file name in section <%s>", namebuf);
	nbexit(EXIT_DB);
  }
  if (sysentry->outname == NULL) {
	prnerr("need output file name in section <%s>", namebuf);
	nbexit(EXIT_DB);
  }
  free(namebuf);
}



/*
 * Write one entry into the database file
 */
void putdb __F((name, sysentry), const char *name AND struct sysdef *sysentry)
{
  char *sectname;
  int len;

  /* Check if we have something to write at all */
  if (name == NULL || (len = strlen(name)) == 0)
	return;

  /* Copy the boot definition into our own static data area */
  if (sysentry != &dbdata)
	dbdata = *sysentry;

  /* Generate section name */
  sectname = (char *)nbmalloc(len + strlen(DBSECTNAME) + 3);
  sprintf(sectname, "%s:%s", DBSECTNAME, name);

  /* Remove any default values */
  if (dbdata.kname != NULL && !strcmp(dbdata.kname, DFLT_IMAGE)) {
	free(dbdata.kname);
	dbdata.kname = NULL;
  }
  if (dbdata.nfsdir != NULL && !strcmp(dbdata.nfsdir, DFLT_DIR)){
	free(dbdata.nfsdir);
	dbdata.nfsdir = NULL;
  }
  if (dbdata.addrs != NULL) {
	if (!strcmp(dbdata.addrs, KERNEL_ADDRS))
		copystr(&dbdata.addrs, "kernel");
	else if (!strcmp(dbdata.addrs, DFLT_ADDRS)) {
		free(dbdata.addrs);
		dbdata.addrs = NULL;
	}
  }

  /* Write section into database */
  if (!opensysdb(FALSE))
	nbexit(-1);
  if (!writesysdb(sectname, dbparams))
	nbexit(-1);
  closesysdb();
  free(sectname);
}

