/*
 **************************************************************************
 *
 * Boot-ROM-Code to load an operating system across a TCP/IP network.
 *
 * Module:  menu.c
 * Purpose: Display a menu of bootimage choices
 * Entries: domenu
 *
 **************************************************************************
 *
 * Copyright (C) 1995-2003 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: menu.c,v 1.5 2003/01/25 23:29:41 gkminix Exp $
 */


#include <general.h>
#include <memory.h>
#include <kernel/net.h>
#include <kernel/arpa.h>
#include <kernel/romlib.h>
#include "bootpriv.h"
#include "menu.h"


/*
 **************************************************************************
 * 
 * Internal representation of each bootimage description
 */
struct image {
  int		 tagnum;		/* image tag number		*/
  int            labellen;		/* length of label		*/
  int            filenamelen;		/* length of file name		*/
  unsigned char	*label;			/* pointer to label		*/
  unsigned char *filename;		/* pointer to file name		*/
  t_ipaddr	 server;		/* server IP address		*/
  t_ipaddr	 gateway;		/* gateway IP address		*/
};



/*
 **************************************************************************
 * 
 * Global variables:
 */
static struct image imagelist[MENU_IMG_NUM];	/* list of menu items	*/
static t_ipaddr def_server;		/* default server		*/
static t_ipaddr def_gateway;		/* default gateway		*/
static int def_timeout;			/* default keyboard timeout	*/
static int def_choice;			/* default menu choice		*/
static int imagenum;			/* number of menu items		*/



/*
 **************************************************************************
 * 
 * Get a numeric response from the user
 */
static int getselect()
{
  int num;
  int pos;
  int i, ch;

  /* Clear keyboard buffer */
  while (chkkey() >= 0) ;

  /* Main loop */
  num = -1;
  pos = 0;
  while (TRUE) {
	if ((ch = getkey(def_timeout)) < 0 || ch == CHR_CR)
		return(num < 0 ? def_choice : num);
	else if (ch == CHR_ESC)
		return(-1);
	else if (ch == CHR_BS) {
		if (pos > 0) {
			printf("\b \b");
			num = num / 10;
			pos--;
		}
		if (pos == 0)
			num = -1;
		continue;
	} else if (ch < '0' || ch > '9')
		continue;
	i = ch - '0';
	if (num >= 0)
		i += num * 10;
	if (i >= imagenum)
		continue;
	num = i;
	printf("%c", ch);
	pos++;
  }
}



/*
 **************************************************************************
 * 
 * Decode image file description tag
 */
static int decode_image(tagstr, tagnum)
unsigned char *tagstr;
int            tagnum;
{
  register unsigned char *cp;
  register unsigned char *bp;
  struct image *ip;
  int strlength;
  int i;

  /* Copy tag number */
  ip = &(imagelist[imagenum]);
  ip->tagnum = tagnum;

  /* Decode label */
  cp = tagstr;
  strlength = *cp++;
  ip->label = cp;
  for (i = 0; *cp && *cp != ':' && strlength > 0; cp++, i++, strlength--) ;
  ip->labellen = i;

  /* Skip until next colon */
  for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ;
  if (!*cp || strlength == 0)
	return(FALSE);
  cp++;
  strlength--;

  /* Decode server IP address */
  bp = cp;
  for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ;
  if (!*cp || strlength == 0)
	return(FALSE);
  if (*bp == ':')
	ip->server = n_IP_ANY;
  else if ((ip->server = resolve((char *)bp)) == n_IP_ANY)
	return(FALSE);
  cp++;
  strlength--;

  /* Decode gateway IP address */
  bp = cp;
  for ( ; *cp && *cp != ':' && strlength > 0; cp++, strlength--) ;
  if (!*cp || strlength == 0)
	return(FALSE);
  if (*bp == ':')
	ip->gateway = n_IP_ANY;
  else if ((ip->gateway = resolve((char *)bp)) == n_IP_ANY)
	return(FALSE);
  cp++;
  strlength--;

  /* Decode filename */
  ip->filename = cp;
  for (i = 0;
       *cp && i < MAX_FNAM_LEN - 1 && strlength > 0;
       cp++, i++, strlength--)
	;
  if (*cp && strlength != 0)
	return(FALSE);
  ip->filenamelen = i;

  imagenum++;
  return(TRUE);
}



/*
 **************************************************************************
 * 
 * Decode default parameter tag
 */
static int decode_params(tagstr)
unsigned char *tagstr;
{
  register unsigned char *cp;
  int *resp;
  int strlength;
  int i, offs;

  /* Decode the parameter tag string */
  cp = tagstr;
  strlength = *cp++;
  while (*cp && strlength > 0) {
	/* Check for valid strings */
	if (!memcmp(cp, "timeout=",  8)) {
		offs = 8;
		resp = &def_timeout;
	} else if (!memcmp(cp, "default=", 8)) {
		offs = 8;
		resp = &def_choice;
	} else
		return(FALSE);
	/* Decode any number following the equal sign */
	cp += offs;
	strlength -= offs;
	i = 0;
	while (strlength > 0 && *cp >= '0' && *cp <= '9') {
		i = i * 10 + (*cp++ - '0');
		strlength--;
	}
	if ((*cp && *cp != ':') || strlength < 0)
		return(FALSE);
	*resp = i;
	/* Skip colon */
	cp++;
	strlength--;
  }

  /* Adjust default choice */
  if (def_choice >= MENU_IMG_FIRST) {
	for (i = 0; i < imagenum; i++)
		if (imagelist[i].tagnum == def_choice) {
			def_choice = i;
			break;
		}
  }
  if (def_choice >= imagenum)
	return(FALSE);

  return(TRUE);
}



/*
 **************************************************************************
 * 
 * Display a menu of bootimage choices
 */
int domenu()
{
  register unsigned char *cp;
  struct bootp *bp;
  struct image *ip;
  int i;

  /*
   * Select BOOTP reply record for all further operations
   */
  if ((bp = bootp_bufs[BOOTP_REPLY]) == NULL)
	return(MENU_INVALID);
  cur_bootp_buf = BOOTP_REPLY;

  /*
   * Check the menu magic number in the BOOTP parameter area, including
   * the menu definition version number.
   */
  if ((cp = get_vend(MENU_ID)) == NULL || *cp != 6 ||
      *((unsigned long *)(cp + 1)) != MENU_MAGIC)
	return(MENU_INVALID); 

#if MENU_VER_MINOR > 0
  if (*(cp + 5) != MENU_VER_MAJOR || *(cp + 6) > MENU_VER_MINOR) {
#else
  if (*(cp + 5) != MENU_VER_MAJOR) {
#endif
	printf("\nMENU: Invalid version number\n");
	return(MENU_INVALID);
  }

  /*
   * Initialize the name resolver to allow for symbolic names in BOOTP
   * tags.
   */
  res_config();

  /*
   * Initialize the default image description from the BOOTP block. This
   * is necessary to be able to restore the BOOTP block with repetitive
   * calls to this function.
   */
  def_server = bp->bp_siaddr;
  def_gateway = n_IP_ANY;
  if((cp = get_vend(VEND_ROUTER)) != NULL)
	def_gateway = *((t_ipaddr *)(cp + 1));

  /* Decode the image file descriptions */
  imagenum = 0;
  for (i = MENU_IMG_FIRST; i <= MENU_IMG_LAST; i++)
	if ((cp = get_vend(i)) != NULL && !decode_image(cp, i)) {
		printf("\nMENU: Invalid image description %d\n", i);
		return(MENU_INVALID);
	}

  /* Decode the default parameter tag */
  if ((cp = get_vend(MENU_PARAMS)) != NULL && !decode_params(cp)) {
	printf("\nMENU: Invalid TAG %d\n", MENU_PARAMS);
	return(MENU_INVALID);
  }

  /* Print the message strings */
  printf("\n\n");
  for (i = MENU_DISP_FIRST; i <= MENU_DISP_LAST; i++)
	if ((cp = get_vend(i)) != NULL) {
		int len = *cp++;
		printf("%ls\n", cp, len);
	}

  /* Print the menu and wait for a user keypress */
  if (imagenum > 0) {
	printf("\n\n");
	for (i = 0; i < imagenum; i++)
		printf("[%s%d]\t%ls\n", i < 10 ? " ":"", i,
				imagelist[i].label, imagelist[i].labellen);
	printf("\nSelect a choice: ");
	i = getselect();
	printf("\n\n");

	/* If no filename specified for image, abort menu selection */
	ip = &(imagelist[i]);
	if (i < 0 || !ip->filenamelen)
		return(MENU_ABORT);

	/* Set bootimage file name */
	memset(bp->bp_file, 0, sizeof(bp->bp_file));
	memcpy(bp->bp_file, ip->filename, ip->filenamelen);

	/* Set IP of tftp server */
	memset(bp->bp_sname, 0, sizeof(bp->bp_sname));
	if (ip->server != n_IP_ANY)
		bp->bp_siaddr = ip->server;
	else
		bp->bp_siaddr = def_server;

	/* Set gateway IP to reach the server */
	if (ip->gateway != n_IP_ANY)
		def_gateway = ip->gateway;
	if ((cp = get_vend(VEND_ROUTER)) != NULL)
		*((t_ipaddr *)(cp + 1)) = def_gateway;
	else if ((cp = get_vend(VEND_END)) != NULL &&
	         (cp + IP_ALEN + 2) <= ((unsigned char *)bp) +
						bootp_sizes[cur_bootp_buf]) {
		*(cp - 1)               = VEND_ROUTER;
		*cp                     = IP_ALEN;
		*((t_ipaddr *)(cp + 1)) = def_gateway;
		*(cp + IP_ALEN + 1)     = VEND_END;
	}
	printf("Selected %ls\n\n", ip->label, ip->labellen);
  }

  return(MENU_OK);
}



syntax highlighted by Code2HTML, v. 0.9.1