/**
 * The sleep_func was intended to make the server sleep for some seconds.
 * This function is currently ignored as making the server sleep actually
 * stalls it and disrupts other clients.
 *
 *\verbatim
 * Usage: sleep <seconds>
 *\endverbatim
 */
int
sleep_func(Client *c, int argc, char **argv)
{
	int secs;
	long out;
	char *endptr;

#define MAX_SECS 60
#define MIN_SECS 1

	if (c->state != ACTIVE)
		return 1;

	if (argc != 2) {
		sock_send_error(c->sock, "Usage: sleep <secs>\n");
		return 0;
	}

	/* set errno to be able to detect errors in strtol() */
	errno = 0;

	out = strtol(argv[1], &endptr, 0);

	/* From the man page for strtol(3)
	 *
	 * In particular, if *nptr is not `\0' but **endptr is
	 * `\0' on return, the entire string is valid.
	 *
	 * In this case, argv[1] is *nptr, and &endptr is **endptr.
	 */

	if (errno) {
		sock_printf_error(c->sock, "number argument: %s\n", strerror(errno));
		return 0;
	}
	else if ((*argv[1] != '\0') && (*endptr == '\0')) {
		/* limit seconds to range: MIN_SECS - MAX_SECS */
		out = (out > MAX_SECS) ? MAX_SECS : out;
		out = (out < MIN_SECS) ? MIN_SECS : out;
		secs = out;
	}
	else {
		sock_send_error(c->sock, "invalid parameter...\n");
		return 0;
	}

	/* Repeat until no more remains - should normally be zero
	 * on exit the first time...*/
	sock_printf(c->sock, "sleeping %d seconds\n", secs);

	/* whoops.... if this takes place as planned, ALL screens
	 * will "freeze" for the alloted time...
	 *
	 * while ((secs = sleep(secs)) > 0)
	 */	;

	sock_send_error(c->sock, "ignored (not fully implemented)\n");
	return 0;
}
Example #2
0
/*************************************************************************
 * menu_del_item_func
 *
 * Deletes an item from a menu
 *
 * Usage: menu_del_item <menuid> <itemid>
 * The given item in the given menu will be deleted. If you have deleted all
 * the items from your client menu, that menu will automatically be removed.
 */
int
menu_del_item_func (Client * c, int argc, char **argv)
{
	Menu * menu;
	MenuItem * item;
	char * menu_id;
	char * item_id;

	if (!c->ack)
		return 1;

	if (argc != 3 ) {
		sock_send_error(c->sock, "Usage: menu_del_item <menuid> <itemid>\n");
		return 0;
	}

	menu_id = argv[1];
	item_id = argv[2];

	/* Does the client have a menu already ? */
	if (!c->menu) {
		sock_send_error(c->sock, "Client has no menu\n");
		return 0;
	}

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	} else {
		/* A specified menu */
		menu = menu_find_item (c->menu, menu_id, true);
	}
	if (!menu) {
		sock_send_error(c->sock, "Cannot find menu id\n");
		return 0;
	}

	item = menu_find_item (c->menu, item_id, true);
	if (!item) {
		sock_send_error(c->sock, "Cannot find item\n");
		return 0;
	}
	menuscreen_inform_item_destruction (item);
	menu_remove_item (menu, item);
	menuscreen_inform_item_modified (item->parent);
	menuitem_destroy (item);

	/* Was it the last item in the client's menu ? */
	if (menu_getfirst_item(c->menu) == NULL) {
		menuscreen_inform_item_destruction (c->menu);
		menu_remove_item (main_menu, c->menu);
		menuscreen_inform_item_modified (main_menu);
		menu_destroy (c->menu);
		c->menu = NULL;
	}
	sock_send_string(c->sock, "success\n");
	return 0;
}
/**
 * Sets the state of the output port (such as on MtxOrb LCDs)
 *
 *\verbatim
 * Usage: output <on|off|int>
 *\endverbatim
 */
int
output_func(Client *c, int argc, char **argv)
{
	if (c->state != ACTIVE)
		return 1;

	if (argc != 2) {
		sock_send_error(c->sock, "Usage: output {on|off|<num>}\n");
		return 0;
	}

	if (0 == strcmp(argv[1], "on"))
		output_state = ALL_OUTPUTS_ON;
	else if (0 == strcmp(argv[1], "off"))
		output_state = ALL_OUTPUTS_OFF;
	else {
		long out;
		char *endptr;

		/* Note that there is no valid range set for
		 * output_state; thus a value in the 12 digits
		 * is not considered out of range.
		 */

		/* set errno to be able to detect errors in strtol() */
		errno = 0;

		out = strtol(argv[1], &endptr, 0);

		if (errno) {
			sock_printf_error(c->sock, "number argument: %s\n", strerror(errno));
			return 0;
		}
		else if ((*argv[1] != '\0') && (*endptr == '\0')) {
			output_state = out;
		}
		else {
			sock_send_error(c->sock, "invalid parameter...\n");
			return 0;
		}
	}

	sock_send_string(c->sock, "success\n");

	/* Makes sense to me to set the output immediately;
	 * however, the outputs are currently set in
	 * draw_screen(screen *s, int timer)
	 * Whatever for? */

	/* drivers_output(output_state); */

	report(RPT_NOTICE, "output states changed");
	return 0;
}
Example #4
0
/***************************************************************
 * Requests the menu system to set the entry point into the menu system. 
 *
 * Usage: menu_set_main <id>
 */
int
menu_set_main_func (Client * c, int argc, char **argv)
{
	char * menu_id;
	Menu * menu;

	debug (RPT_DEBUG, "%s( Client [%d], %s, %s )",
	       __FUNCTION__, c->sock, (argc > 1 ? argv[1] : "<null>"),
	       (argc > 2 ? argv[2] : "<null>") );
	if (!c->ack)
		return 1;

	if (argc != 2 ) {
		sock_send_error(c->sock, "Usage: menu_set_main <menuid>\n");
		return 0;
	}

	menu_id = argv[1];

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	}
	else if (strcmp(menu_id, "_main_") == 0) {
		menu = NULL;
	}
	else {
		/* A specified menu */
		menu = menu_find_item (c->menu, menu_id, true);
		if ( ! menu) {
			sock_send_error(c->sock, "Cannot find menu id\n");
			return 0;
		}
	}

	menuscreen_set_main(menu);

	sock_send_string(c->sock, "success\n");
	return 0;
}
Example #5
0
/***************************************************************
 * Requests the menu system to display the given menu screen.  Depending on
 * the setting of the LCDPROC_PERMISSIVE_MENU_GOTO it is impossible
 * to go to a menu of another client (or the server menus). Same
 * restriction applies to the optional predecessor_id
 *
 * Usage: menu_goto <id> [<predecessor_id>]
 */
int
menu_goto_func (Client * c, int argc, char **argv)
{
	char * menu_id;
	Menu * menu;

	debug (RPT_DEBUG, "%s( Client [%d], %s, %s )",
	       __FUNCTION__, c->sock, (argc > 1 ? argv[1] : "<null>"),
	       (argc > 2 ? argv[2] : "<null>") );
	if (!c->ack)
		return 1;

	if ((argc < 2 ) || (argc > 3)) {
		sock_send_error(c->sock, "Usage: menu_goto <menuid> [<predecessor_id>]\n");
		return 0;
	}

	menu_id = argv[1];

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	} else {
		/* A specified menu */
		menu = menuitem_search(menu_id, c);
	}

	if (!menu) {
		sock_send_error(c->sock, "Cannot find menu id\n");
		return 0;
	}

	if (argc > 2 )
		set_predecessor(menu, argv[2], c);

	menuscreen_goto (menu);
	/* Failure is not returned (Robijn) */
	sock_send_string(c->sock, "success\n");
	return 0;
}
/**
 * The client requests that the server forget about a screen
 *
 *\verbatim
 * Usage: screen_del <screenid>
 *\endverbatim
 */
int
screen_del_func(Client *c, int argc, char **argv)
{
	int err = 0;
	Screen *s;

	if (c->state != ACTIVE)
		return 1;

	if (argc != 2) {
		sock_send_error(c->sock, "Usage: screen_del <screenid>\n");
		return 0;
	}

	debug(RPT_DEBUG, "screen_del: Deleting screen %s", argv[1]);

	s = client_find_screen(c, argv[1]);
	if (s == NULL) {
		sock_send_error(c->sock, "Unknown screen id\n");
		return 0;
	}

	err = client_remove_screen(c, s);
	if (err == 0) {
		sock_send_string(c->sock, "success\n");
	}
	else if (err < 0) {
		sock_send_error(c->sock, "failed to remove screen\n");
	}
	else {
		sock_send_error(c->sock, "Unknown screen id\n");
	}

	report(RPT_INFO, "Client on socket %d removed screen \"%s\"", c->sock, s->id);

	screen_destroy(s);
	return 0;
}
/**
 * Tells the server the client has another screen to offer
 *
 *\verbatim
 * Usage: screen_add <id>
 *\endverbatim
 */
int
screen_add_func(Client *c, int argc, char **argv)
{
	int err = 0;
	Screen *s;

	if (c->state != ACTIVE)
		return 1;

	if (argc != 2) {
		sock_send_error(c->sock, "Usage: screen_add <screenid>\n");
		return 0;
	}

	debug(RPT_DEBUG, "screen_add: Adding screen %s", argv[1]);

	s = client_find_screen(c, argv[1]);
	if (s != NULL) {
		sock_send_error(c->sock, "Screen already exists\n");
		return 0;
	}

	s = screen_create(argv[1], c);
	if (s == NULL) {
		sock_send_error(c->sock, "failed to create screen\n");
		return 0;
	}

	err = client_add_screen(c, s);

	if (err == 0) {
		sock_send_string(c->sock, "success\n");
	} else {
		sock_send_error(c->sock, "failed to add screen\n");
	}
	report(RPT_INFO, "Client on socket %d added added screen \"%s\"", c->sock, s->id);
	return 0;
}
Example #8
0
static int parse_message(const char *str, Client *c)
{
	typedef enum { ST_INITIAL, ST_WHITESPACE, ST_ARGUMENT, ST_FINAL } State;
	State state = ST_INITIAL;

	int error = 0;
	char quote = '\0';	/* The quote used to open a quote string */
	int pos = 0;
	char *arg_space;
	int argc = 0;
	char *argv[MAX_ARGUMENTS];
	int argpos = 0;
	CommandFunc function = NULL;

	debug(RPT_DEBUG, "%s(str=\"%.120s\", client=[%d])", __FUNCTION__, str, c->sock);

	/* We will create a list of strings that is shorter or equally long as
	 * the original string str.
	 */
	arg_space = malloc(strlen(str)+1);
	if (arg_space == NULL) {
		report(RPT_ERR, "%s: Could not allocate memory", __FUNCTION__);
		sock_send_error(c->sock, "error allocating memory!\n");
	}

	argv[0] = arg_space;

	while ((state != ST_FINAL) && !error) {
		char ch = str[pos++];

		switch (state) {
		  case ST_INITIAL:
		  case ST_WHITESPACE:
			if (is_whitespace(ch))
				break;
			if (is_final(ch)) {
				state = ST_FINAL;
				break;
			}	  
			/* otherwise fall through */
			state = ST_ARGUMENT;
		  case ST_ARGUMENT:
			if (is_final(ch)) {
				if (quote)
					error = 2;
				if (argc >= MAX_ARGUMENTS-1) {
					error = 1;
				}
				else {
					argv[argc][argpos] = '\0';
					argv[argc+1] = argv[argc] + argpos + 1;
					argc++;
					argpos = 0;
				}
				state = ST_FINAL;
			}
			else if (ch == '\\') {
			 	if (str[pos]) {
			 		/* We solve quoted chars here right away */
					const char escape_chars[] = "nrt";
					const char escape_trans[] = "\n\r\t";
			 		char *p = strchr(escape_chars, str[pos]);

					/* Is it wise to have the characters \n, \r & \t expanded ?
					 * Can the displays deal with them ?
					 */
					if (p != NULL) {
						/* Insert a replacement for the code */
						argv[argc][argpos++] = escape_trans[p - escape_chars];
					}
					else {
						 /* Copy char literally */
						argv[argc][argpos++] = str[pos];
					}
					pos++;
			 	}
			 	else {
			 		error = 2;
					/* alternative: argv[argc][argpos++] = ch; */
					if (argc >= MAX_ARGUMENTS-1) {
						error = 1;
					}
					else {
						argv[argc][argpos] = '\0';
						argv[argc+1] = argv[argc] + argpos + 1;
						argc++;
						argpos = 0;
					}
			 		state = ST_FINAL;
			 	}
			}
			else if (is_opening_quote(ch, quote)) {
				quote = ch;
			}	
			else if (is_closing_quote(ch, quote)) {
				quote = '\0';
				if (argc >= MAX_ARGUMENTS-1) {
					error = 1;
				}
				else {
					argv[argc][argpos] = '\0';
					argv[argc+1] = argv[argc] + argpos + 1;
					argc++;
					argpos = 0;
				}
				state = ST_WHITESPACE;
			}
			else if (is_whitespace(ch) && (quote == '\0')) {
				if (argc >= MAX_ARGUMENTS-1) {
					error = 1;
				}
				else {
					argv[argc][argpos] = '\0';
					argv[argc+1] = argv[argc] + argpos + 1;
					argc++;
					argpos = 0;
				}
				state = ST_WHITESPACE;
			}	
			else {
				argv[argc][argpos++] = ch;
			}	
			break;
		  case ST_FINAL:
		  	/* This will never be reached */
			break;
		}
	}
	if (argc < MAX_ARGUMENTS)
		argv[argc] = NULL;
	else
		error = 1;

	if (error) {
		sock_send_error(c->sock, "Could not parse command\n");
		free(arg_space);
		return 0;
	}

#if 0 /* show what we have parsed */
	int i;
	for (i = 0; i < argc; i++) {
		printf("%s%c", argv[i], (i == argc-1) ? '\n' : ' ');
	}	
#endif

	/* Now find and call the appropriate function...*/
	function = get_command_function(argv[0]);

	if (function != NULL) {
		error = function(c, argc, argv);
		if (error) {
			sock_printf_error(c->sock, "Function returned error \"%.40s\"\n", argv[0]);
			report(RPT_WARNING, "Command function returned an error after command from client on socket %d: %.40s", c->sock, str);
		}	
	}
	else {
		sock_printf_error(c->sock, "Invalid command \"%.40s\"\n", argv[0]);
		report(RPT_WARNING, "Invalid command from client on socket %d: %.40s", c->sock, str);
	}

	free(arg_space);
	return 0;
}
Example #9
0
/*************************************************************************
 * menu_add_item_func
 *
 * Adds an item to a menu
 *
 * Usage: menu_add_item <menuid> <newitemid> <type> [<text>]
 * You should use "" as id for the client's main menu. This menu will be
 * created automatically when you add an item to it the first time.
 * You (currently?) cannot create a menu in the main level yourself.
 * The names you use for items should be unique for your client.
 * The text is the visible text for the item.
 *
 * The following types are available:
 * - menu
 * - action
 * - checkbox
 * - ring (a kind of listbox of one line)
 * - slider
 * - numeric
 * - alpha
 * - ip
 */
int
menu_add_item_func (Client * c, int argc, char **argv)
{
	char * menu_id;
	char * item_id;
	char * text = NULL;
	Menu * menu = NULL;
	MenuItem * item;
	MenuItemType itemtype;
	char** argv_set = NULL;

	debug (RPT_DEBUG, "%s( Client [%d], %s, %s )",
	       __FUNCTION__, c->sock, argv[1], argv[2]);
	if (!c->ack)
		return 1;

	if (!c->name) {
		sock_send_error(c->sock, "You need to give your client a name first\n");
		return 0;
	}

	if ((argc < 4 )) {
		sock_send_error(c->sock, "Usage: menu_add_item <menuid> <newitemid> <type> [<text>]\n");
		return 0;
	}

	menu_id = argv[1];
	item_id = argv[2];

	/* Does the client have a menu already ? */
	if (!c->menu) {
		/* We need to create it */
		report( RPT_INFO, "Client [%d] is using the menu", c->sock );
		c->menu = menu_create ("_client_menu_", menu_commands_handler, c->name, c);
		menu_add_item (main_menu, c->menu);
	}

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	} else {
		/* A specified menu */
		menu = menu_find_item (c->menu, menu_id, true);
	}
	if (!menu) {
		sock_send_error(c->sock, "Cannot find menu id\n");
		return 0;
	}

	item = menu_find_item (c->menu, item_id, true);
	if (item) {
		sock_send_error(c->sock, "Item id already in use\n");
		return 0;
	}

	/* Find menuitem type */
	itemtype = menuitem_typename_to_type (argv[3]);
	if (itemtype == -1) {
		sock_send_error(c->sock, "Invalid menuitem type\n");
		return 0;
	}

	/* Is a text given (options don't count)? */
	if (argc >= 5 && argv[4][0] != '-') {
		text = argv[4];
	}
	else {
		text = "";
	}

	/* Create the menuitem */
	switch (itemtype) {
	  case MENUITEM_MENU:
		item = menu_create (item_id, menu_commands_handler, text, c);
		break;
	  case MENUITEM_ACTION:
		item = menuitem_create_action (item_id, menu_commands_handler, text, c,
				MENURESULT_NONE);
		break;
	  case MENUITEM_CHECKBOX:
		item = menuitem_create_checkbox (item_id, menu_commands_handler, text, c,
				false, false);
		break;
	  case MENUITEM_RING:
		item = menuitem_create_ring (item_id, menu_commands_handler, text, c,
				"", 0);
		break;
	  case MENUITEM_SLIDER:
		item = menuitem_create_slider (item_id, menu_commands_handler, text, c,
				 "", "", 0, 100, 1, 25);
		break;
	  case MENUITEM_NUMERIC:
		item = menuitem_create_numeric (item_id, menu_commands_handler, text, c,
				0, 100, 0);
		break;
	  case MENUITEM_ALPHA:
		item = menuitem_create_alpha (item_id, menu_commands_handler, text, c,
				0, 0, 10, true, false, true, "-./", "");
		break;
	  case MENUITEM_IP:
		item = menuitem_create_ip (item_id, menu_commands_handler, text, c,
				0, "192.168.1.245");
		break;
	 default:
	   assert(!"unexpected menuitem type");
	}
	menu_add_item (menu, item);
	menuscreen_inform_item_modified (menu);
	sock_send_string(c->sock, "success\n");

	/* are there any options (starting with '-')?
	 * - create a temporary argv for menu_set_item() call */
	if (argc > 5 || argv[4][0] == '-') {
		// menu_add_item <menuid> <newitemid> <type> [<text>]
		// menu_set_item <menuid> <itemid> {<option>}+
		int i, j;
		argv_set = malloc(argc * sizeof(char*));
		assert(argv_set);
		argv_set[0] = "menu_set_item";
		for (i = j = 1; i < argc; ++i)
		{
			/* skip "type" */
			if (i == 3)
				continue;
			/* skip "text" */
			if (i == 4 && argv[4][0] != '-')
				continue;

			argv_set[j++] = argv[i];
		}
		menu_set_item_func(c, j, argv_set);
		free(argv_set);
	}
	return 0;
}
Example #10
0
/**************************************************************************
 * menu_set_item_func
 * Sets the info about a menu item
 *
 * For example, text displayed, value, etc...
 *
 * Usage: menu_set_item <menuid> <itemid> {<option>}+
 * The following parameters can be set per item:
 * (you should include the - in the option)
 *
 * For all types:
 * -text "text"			("")
 *	Sets the visible text.
 * -is_hidden false|true	(false)
 *	If the item currently should not appear in a menu.
 * -prev id			()
 *	Sets the predecessor of this item (what happens after "Escape")
 *
 * For all except menus:
 * -next id			()
 *	Sets the successor of this item (what happens after "Enter")
 *
 * action:
 * -menu_result none|close|quit	(none)
 *	Sets what to do with the menu when this action is selected:
 *	- none: the menu stays as it is.
 *	- close: the menu closes and returns to a higher level.
 *	- quit: quits the menu completely so you can foreground your app.
 *
 * checkbox:
 * -value off|on|gray		(off)
 *	Sets its current value.
 * -allow_gray false|true	(false)
 *	Sets if a grayed checkbox is allowed.
 *
 * ring:
 * -value <int>			(0)
 *	Sets the index in the stringlist that is currently selected.
 * -strings <string>		(empty)
 *	The subsequent strings that can be selected. They should be
 *	tab-separated in ONE string.
 *
 * slider:
 * -value <int>			(0)
 *	Sets its current value.
 * -mintext <string>		("")
 * -maxtex <string>		("")
 *	Text at the minimal and maximal side. On small displays these might
 *	not be displayed.
 * -minvalue <int>		(0)
 * -maxvalue <int>		(100)
 *	The minimum and maximum value of the slider.
 * -stepsize <int>		(1)
 *	The stepsize of the slider. If you use 0, you can control it yourself
 *	completely.
 *
 * numeric:
 * -value <int>			(0)
 *	Sets its current value.
 * -minvalue <int>		(0)
 * -maxvalue <int>		(100)
 *	The minimum and maximum value that are allowed. If you make one of
 *	them negative, the user will be able to enter negative numbers too.
 * Maybe floats will work too in the future.
 *
 * alpha:
 * -value <string>
 *	Sets its current value.	("")
 * -password_char <char>	(none)
 * -minlength <int>		(0)
 * -maxlength <int>		(10)
 *	Set the minimum and maximum allowed length.
 * -allow_caps false|true	(true)
 * -allow_noncaps false|true	(false)
 * -allow_numbers false|true	(true)
 *	Allows these groups of characters.
 * -allowed_extra <string>	("")
 *	The chars in this string are also allowed.
 *
 * ip:
 * -value <string>
 *	Sets its current value.	("")
 * -v6 false|true
 *
 * Hmm, this is getting very big. We might need a some real parser after all.
 */
int
menu_set_item_func (Client * c, int argc, char **argv)
{
	typedef enum AttrType { NOVALUE, BOOLEAN, CHECKBOX_VALUE, SHORT, INT,
				FLOAT, STRING } AttrType;

	/* This table generalizes the options.
	 * The table lists which options can exist for which menu items,
	 * what kind of parameter they should have and where this scanned
	 * parameter should be stored.
	 */
	struct OptionTable {
		MenuItemType menuitem_type;	/* For what MenuItem type is
						   the option ?
						   Use -1 for ALL types. */
		char * name;			/* The option concerned */
		AttrType attr_type;		/* Type of value */
		int attr_offset;		/* Where to put the value
						   in the structure.
						   Use -1 to process it
						   yourself. */
		/* Watch out with STRING, it will free() the current value
		 * and reallocate for the new value !! If you don't want
		 * that, use -1 for offset, to process it yourself. */
	} option_table[] = {
		{ -1,			"text",		STRING,		offsetof(MenuItem,text) },
		{ -1,			"is_hidden",	BOOLEAN,	offsetof(MenuItem,is_hidden) },
		{ -1,			"prev",	STRING,		-1 },
		{ -1,			"next",		STRING,		-1 },
		{ MENUITEM_ACTION,	"menu_result",	STRING,		-1 },
		{ MENUITEM_CHECKBOX,	"value",	CHECKBOX_VALUE,	offsetof(MenuItem,data.checkbox.value) },
		{ MENUITEM_CHECKBOX,	"allow_gray",	BOOLEAN,	offsetof(MenuItem,data.checkbox.allow_gray) },
		{ MENUITEM_RING,	"value",	SHORT,		offsetof(MenuItem,data.ring.value) },
		{ MENUITEM_RING,	"strings",	STRING,		-1 },
		{ MENUITEM_SLIDER,	"value",	INT,		offsetof(MenuItem,data.slider.value) },
		{ MENUITEM_SLIDER,	"minvalue",	INT,		offsetof(MenuItem,data.slider.minvalue) },
		{ MENUITEM_SLIDER,	"maxvalue",	INT,		offsetof(MenuItem,data.slider.maxvalue) },
		{ MENUITEM_SLIDER,	"stepsize",	INT,		offsetof(MenuItem,data.slider.stepsize) },
		{ MENUITEM_SLIDER,	"mintext",	STRING,		offsetof(MenuItem,data.slider.mintext) },
		{ MENUITEM_SLIDER,	"maxtext",	STRING,		offsetof(MenuItem,data.slider.maxtext) },
		{ MENUITEM_NUMERIC,	"value",	INT,		offsetof(MenuItem,data.numeric.value) },
		{ MENUITEM_NUMERIC,	"minvalue",	INT,		offsetof(MenuItem,data.numeric.minvalue) },
		{ MENUITEM_NUMERIC,	"maxvalue",	INT,		offsetof(MenuItem,data.numeric.maxvalue) },
		/*{ MENUITEM_NUMERIC,	"allow_decimals",BOOLEAN,	offsetof(MenuItem,data.numeric.allow_decimals) },*/
		{ MENUITEM_ALPHA,	"value",	STRING,		-1 /*offsetof(MenuItem,data.alpha.value)*/ },
		{ MENUITEM_ALPHA,	"minlength",	SHORT,		offsetof(MenuItem,data.alpha.minlength) },
		{ MENUITEM_ALPHA,	"maxlength",	SHORT,		offsetof(MenuItem,data.alpha.maxlength) },
		{ MENUITEM_ALPHA,	"password_char",STRING,		-1 },
		{ MENUITEM_ALPHA,	"allow_caps",	BOOLEAN,	offsetof(MenuItem,data.alpha.allow_caps) },
		{ MENUITEM_ALPHA,	"allow_noncaps",BOOLEAN,	offsetof(MenuItem,data.alpha.allow_noncaps) },
		{ MENUITEM_ALPHA,	"allow_numbers",BOOLEAN,	offsetof(MenuItem,data.alpha.allow_numbers) },
		{ MENUITEM_ALPHA,	"allowed_extra",STRING,		offsetof(MenuItem,data.alpha.allowed_extra) },
		{ MENUITEM_IP,		"v6",		BOOLEAN,	offsetof(MenuItem,data.ip.v6) },
		{ MENUITEM_IP,		"value",	STRING,		-1 /*offsetof(MenuItem,data.ip.value)*/ },
		{ -1,			NULL,		-1,		-1 }
	};

	debug (RPT_DEBUG, "%s( Client [%d]: %s)",
	       __FUNCTION__, c->sock, argv2string(argc, argv));
	bool bool_value = false;
	CheckboxValue checkbox_value = CHECKBOX_OFF;
	short short_value = 0;
	int int_value = 0;
	float float_value = 0;
	char * string_value = NULL;

	Menu * menu;
	MenuItem * item;
	char * menu_id;
	char * item_id;
	int argnr;

	if (!c->ack)
		return 1;

	if (argc < 4 ) {
		sock_send_error(c->sock, "Usage: menu_set_item <menuid> <itemid> {<option>}+\n");
		return 0;
	}

	menu_id = argv[1];
	item_id = argv[2];

	if ( menu_id[0] == 0 ) {
		/* No menu specified = client's main menu */
		menu = c->menu;
	} else {
		/* A specified menu */
		menu = menu_find_item (c->menu, menu_id, true);
	}
	if (!menu) {
		sock_send_error(c->sock, "Cannot find menu id\n");
		return 0;
	}

	item = menu_find_item (c->menu, item_id, true);
	if (!item) {
		sock_send_error(c->sock, "Cannot find item\n");
		return 0;
	}

	/* Scan all arguments */
	for( argnr = 3; argnr < argc; argnr ++) {
		int option_nr = -1;
		int found_option_name = 0;
		int error = 0;
		void * location;
		char * p;

		/* Find the option in the table */
		if( argv[argnr][0] == '-' ) {
			int i;
			for( i=0; option_table[i].name; i++ ) {
				if( strcmp( argv[argnr]+1, option_table[i].name ) == 0 ) {
					found_option_name = 1;
					if( item->type == option_table[i].menuitem_type
					|| option_table[i].menuitem_type == -1 ) {
						option_nr = i;
					}
				}
			}
		}
		else {
			sock_printf_error(c->sock, "Found non-option: \"%.40s\"\n", argv[argnr]);
			continue; /* Skip to next arg */
		}
		if( option_nr == -1 ) {
			if( found_option_name ) {
				sock_printf_error(c->sock, "Option not valid for menuitem type: \"%.40s\"\n", argv[argnr]);
			} else {
				sock_printf_error(c->sock, "Unknown option: \"%.40s\"\n", argv[argnr]);
			}
			continue; /* Skip to next arg */
		}

		/* OK, we now know we have an option that is valid for the item type. */

		/* Check for value */
		if( option_table[option_nr].attr_type != NOVALUE ) {
			if( argnr + 1 >= argc ) {
				sock_printf_error(c->sock, "Missing value at option: \"%.40s\"\n", argv[argnr]);
				continue; /* Skip to next arg (probably is not existing :) */
			}
		}
		/* Process the value that goes with the option */
		location = (void*)item + option_table[option_nr].attr_offset;
		switch( option_table[option_nr].attr_type ) {
		  case NOVALUE:
			break;
		  case BOOLEAN:
			if( strcmp( argv[argnr+1], "false" ) == 0 ) {
				bool_value = false;
			} else if( strcmp( argv[argnr+1], "true" ) == 0 ) {
				bool_value = true;
			} else {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(bool *)location = bool_value;
			}
			break;
		  case CHECKBOX_VALUE:
			if( strcmp( argv[argnr+1], "off" ) == 0 ) {
				checkbox_value = CHECKBOX_OFF;
			} else if( strcmp( argv[argnr+1], "on" ) == 0 ) {
				checkbox_value = CHECKBOX_ON;
			} else if( strcmp( argv[argnr+1], "gray" ) == 0 ) {
				checkbox_value = CHECKBOX_GRAY;
			} else {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(CheckboxValue *)location = checkbox_value;
			}
			break;
		  case SHORT:
			short_value = strtol( argv[argnr+1], &p, 0 );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(short*)location = short_value;
			}
			break;
		  case INT:
			int_value = strtol( argv[argnr+1], &p, 0 );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(int*)location = int_value;
			}
			break;
		  case FLOAT:
			float_value = strtod( argv[argnr+1], &p );
			if( argv[argnr+1][0] == '\0' || *p != '\0' ) {
				error = 1;
				break;
			}
			if( option_table[option_nr].attr_offset != -1 ) {
				*(float*)location = float_value;
			}
			break;
		  case STRING:
		  	string_value = argv[argnr+1];
			if( option_table[option_nr].attr_offset != -1 ) {
				free( *(char**)location );
				*(char**)location = strdup( string_value );
			}
			else if (strcmp(argv[argnr], "-prev") == 0) {
				set_predecessor(item, string_value, c);
			}
			else if (strcmp(argv[argnr], "-next") == 0) {
				set_successor(item, string_value, c);
			}
			break;
		}
		switch( error ) {
		  case 1:
			sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]);
			argnr ++;
			continue; /* Skip current option and the invalid value */
		}

		/* And at last process extra things for certain options.
		 * Most useful for the attr_offset==-1 stuff. */
		switch (item->type) {
		  case MENUITEM_ACTION:
			if( strcmp( argv[argnr]+1, "menu_result" ) == 0 ) {
				if( strcmp( argv[argnr+1], "none" ) == 0 ) {
					set_successor(item, "_none_", c);
				} else if( strcmp( argv[argnr+1], "close" ) == 0 ) {
					set_successor(item, "_close_", c);
				} else if( strcmp( argv[argnr+1], "quit" ) == 0 ) {
					set_successor(item, "_quit_", c);
				} else {
					error = 1;
				}
			}
			break;
		  case MENUITEM_SLIDER:
			if( item->data.slider.value < item->data.slider.minvalue ) {
				item->data.slider.value = item->data.slider.minvalue;
			} else if( item->data.slider.value > item->data.slider.maxvalue ) {
				item->data.slider.value = item->data.slider.maxvalue;
			}
			break;
		  case MENUITEM_RING:
		  	if( strcmp( argv[argnr]+1, "strings" ) == 0 ) {
		  		free (item->data.ring.strings);
		  		item->data.ring.strings = tablist2linkedlist (string_value);
		  	}
			item->data.ring.value %= LL_Length( item->data.ring.strings );
			break;
		  case MENUITEM_NUMERIC:
			menuitem_reset (item);
			break;
		  case MENUITEM_ALPHA:
			if( strcmp( argv[argnr]+1, "password_char" ) == 0 ) {
				item->data.alpha.password_char = string_value[0];
			}
			else if( strcmp( argv[argnr]+1, "maxlength" ) == 0 ) {
				char * new_buf;
				if( short_value < 0 || short_value > 1000 ) {
					error = 2;
					break;
				}
				new_buf = malloc( short_value + 1 );
				strncpy( new_buf, item->data.alpha.value, short_value );
				new_buf[short_value] = '\0'; /* terminate */
				free( item->data.alpha.value );
				item->data.alpha.value = new_buf;
				free( item->data.alpha.edit_str );
				item->data.alpha.edit_str = malloc( short_value + 1 );
				item->data.alpha.edit_str[0] = '\0';
			}
			else if( strcmp( argv[argnr]+1, "value" ) == 0 ) {
				strncpy( item->data.alpha.value, string_value, item->data.alpha.maxlength );
				item->data.alpha.value[ item->data.alpha.maxlength ] = 0; /* terminate */
			}
			menuitem_reset (item);
			break;
		  case MENUITEM_IP:
			if( strcmp( argv[argnr]+1, "v6" ) == 0 ) {
				char * new_buf;
                		/* set max lenth depending ob boolean option v6 */
				item->data.ip.maxlength = (bool_value == 0) ? 15 : 39;

				new_buf = malloc(item->data.ip.maxlength + 1 );
				strncpy( new_buf, item->data.ip.value, item->data.ip.maxlength);

				new_buf[item->data.ip.maxlength] = '\0'; /* terminate */
				free( item->data.ip.value );
				item->data.ip.value = new_buf;
				free( item->data.ip.edit_str );
				item->data.ip.edit_str = malloc( item->data.ip.maxlength +1);
				item->data.ip.edit_str[0] = '\0';
			}
			else if( strcmp( argv[argnr]+1, "value" ) == 0 ) {
				strncpy( item->data.ip.value, string_value, item->data.ip.maxlength );
				item->data.ip.value[item->data.ip.maxlength] = '\0'; /* terminate */
			}
			menuitem_reset (item);
			break;
		  default:
			break;
		}
		switch( error ) {
		  case 1:
			sock_printf_error(c->sock, "Could not interpret value at option: \"%.40s\"\n", argv[argnr]);
			continue; /* Skip to next arg and retry it as an option */
		  case 2:
			sock_printf_error(c->sock, "Value out of range at option: \"%.40s\"\n", argv[argnr]);
			argnr ++;
			continue; /* Skip current option and the invalid value */
		}
		menuscreen_inform_item_modified (item);
		if( option_table[option_nr].attr_type != NOVALUE ) {
			/* Skip the now used argument */
			argnr ++;
		}
	}
	sock_send_string(c->sock, "success\n");
	return 0;
}
/**
 * Tells the server the client would NOT like to accept keypresses
 * of a particular type when the given screen is active on the display
 *
 *\verbatim
 * Usage: screen_del_key <screenid> <keylist>
 *\endverbatim
 */
int
screen_del_key_func(Client *c, int argc, char **argv)
{
	Screen *s;  /* Attached to a specific screen */
	char *id;  /* Screen ID */
	char *keys;  /* Keys wanted */

	if (c->state != ACTIVE)
		return 1;

	if (argc != 3) {
		switch (argc) {
			case 1:
				sock_send_error(c->sock, "Usage: screen_del_key <screenid> <keylist>\n");
				break;
			case 2:
				sock_send_error(c->sock, "You must specify a key list\n");
				break;
			default:
				sock_send_error(c->sock, "Too many parameters\n");
				break;
		}
		return 0;
	}

	id = argv[1];
	keys = argv[2];
	debug(RPT_DEBUG, "screen_del_key: Deleting key(s) %s from screen %s", keys, id);

	/* Find the screen*/
	s = client_find_screen(c, id);
	if (s == NULL) {
		sock_send_error(c->sock, "Unknown screen id\n");
		return 0;
	}

	/* Do we have keys?*/
	if (s->keys != NULL) {
		/* Have keys, remove keys from the list
		 * NOTE: We let malloc/realloc remember the length
		 *    of the allocated storage.  If keys are later
		 *    added, realloc (in add_key above) will make
		 *    sure there is enough space at s->keys
		 */
		char *from;
		char *to;

		to = from = s->keys;
		while (*from != '\0') {
			/* Is this key to be deleted from the list? */
			if (strchr(keys, *from) == 0) {
				/* Yes, skip it */
				++from;
			}
			else {
				/* No, save it */
				*to++ = *from++;
			}
		}
		to = '\0';	/* terminates the new keys string...*/
	}

	sock_send_string(c->sock, "success\n");

	return 0;
}
/**
 * Tells the server the client would like to accept keypresses
 * of a particular type when the given screen is active on the display
 *
 *\verbatim
 * Usage: screen_add_key <screenid> <keylist>
 *\endverbatim
 */
int
screen_add_key_func(Client *c, int argc, char **argv)
{
	Screen *s;  /* Attached to a specific screen */
	char *id;  /* Screen ID */
	char *keys;  /* Keys wanted */

	if (c->state != ACTIVE)
		return 1;

	if (argc != 3) {
		switch (argc) {
			case 1:
				sock_send_error(c->sock, "Usage: screen_add_key <screenid> <keylist>\n");
				break;
			case 2:
				sock_send_error(c->sock, "You must specify a key list\n");
				break;
			default:
				sock_send_error(c->sock, "Too many parameters...\n");
				break;
		}
		return 0;
	}

	id = argv[1];
	keys = argv[2];
	debug(RPT_DEBUG, "screen_add_key: Adding key(s) %s to screen %s", keys, id);

	/* Find the screen*/
	s = client_find_screen(c, id);
	if (!s) {
		sock_send_error(c->sock, "Unknown screen id\n");
		return 0;
	}

	/* Save the keys*/
	if (s->keys == NULL) {
		/* Save supplied key list*/
		s->keys = strdup(keys);
	}
	else {
		/* Add supplied keys to existing list
		 * NOTE: There could be duplicates in the resulting list
		 *    That's OK, it's the existence of the key in the list
		 *    that's important.  We'll be more careful in the delete
		 *    key function.
		 */
		char *new_keys;
		new_keys = realloc(s->keys, strlen(s->keys) + strlen(keys) +1);
		if (new_keys) {
			strcpy(new_keys, s->keys);
			strcat(new_keys, keys);
			free(s->keys);
			s->keys = new_keys;
		}
		else {
			sock_send_error(c->sock, "Could not add new keys\n");
			return 0;
		}
	}

	if (s->keys == NULL)
		sock_send_error(c->sock, "failed\n");
	else
		sock_send_string(c->sock, "success\n");

	return 0;
}
/**
 * Configures info about a particular screen, such as its
 *  name, priority, or duration
 *
 *\verbatim
 * Usage: screen_set <id> [-name <name>] [-wid <width>] [-hgt <height>]
 *     [-priority <prio>] [-duration <int>] [-timeout <int>]
 *     [-heartbeat <type>] [-backlight <type>]
 *     [-cursor <type>] [-cursor_x <xpos>] [-cursor_y <ypos>]
 *\endverbatim
 */
int
screen_set_func(Client *c, int argc, char **argv)
{
	int i;

	int number;
	char *id;
	Screen * s;

	if (c->state != ACTIVE)
		return 1;

	if (argc == 1) {
		sock_send_error(c->sock, "Usage: screen_set <id> [-name <name>]"
				" [-wid <width>] [-hgt <height>] [-priority <prio>]"
				" [-duration <int>] [-timeout <int>]"
				" [-heartbeat <type>] [-backlight <type>]"
				" [-cursor <type>]"
				" [-cursor_x <xpos>] [-cursor_y <ypos>]\n");
		return 0;
	}
	else if (argc == 2) {
		sock_send_error(c->sock, "What do you want to set?\n");
		return 0;
	}

	id = argv[1];
	s = client_find_screen(c, id);
	if (s == NULL) {
		sock_send_error(c->sock, "Unknown screen id\n");
		return 0;
	}
	/* Handle the rest of the parameters*/
	for (i = 2; i < argc; i++) {
		char *p = argv[i];

		/* ignore leading '-' in options: we allow both forms */
		if (*p == '-')
			p++;

		/* Handle the "name" parameter*/
		if (strcmp(p, "name") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: name=\"%s\"", argv[i]);

				/* set the name...*/
				if (s->name != NULL)
					free(s->name);
				s->name = strdup(argv[i]);
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-name requires a parameter\n");
			}
		}
		/* Handle the "priority" parameter*/
		else if (strcmp(p, "priority") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: priority=\"%s\"", argv[i]);

				/* first try to interpret it as a number */
				number = atoi(argv[i]);
				if (number > 0) {
					if (number <= 64)
						number = PRI_FOREGROUND;
					else if (number < 192)
						number = PRI_INFO;
					else
						number = PRI_BACKGROUND;
				}
				else {
					/* Try if it is a priority class */
					number = screen_pri_name_to_pri(argv[i]);
				}
				if (number >= 0) {
					s->priority = number;
					sock_send_string(c->sock, "success\n");
				}
				else {
					sock_send_error(c->sock, "invalid argument at -priority\n");
				}
			}
			else {
				sock_send_error(c->sock, "-priority requires a parameter\n");
			}
		}
		/* Handle the "duration" parameter*/
		else if (strcmp(p, "duration") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: duration=\"%s\"", argv[i]);

				/* set the duration...*/
				number = atoi(argv[i]);
				if (number > 0)
					s->duration = number;
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-duration requires a parameter\n");
			}
		}
		/* Handle the "heartbeat" parameter*/
		else if (strcmp(p, "heartbeat") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: heartbeat=\"%s\"", argv[i]);

				/* set the heartbeat type...*/
				if (0 == strcmp(argv[i], "on"))
					s->heartbeat = HEARTBEAT_ON;
				else if (0 == strcmp(argv[i], "off"))
					s->heartbeat = HEARTBEAT_OFF;
				else if (0 == strcmp(argv[i], "open"))
					s->heartbeat = HEARTBEAT_OPEN;
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-heartbeat requires a parameter\n");
			}
		}
		/* Handle the "wid" parameter*/
		else if (strcmp(p, "wid") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: wid=\"%s\"", argv[i]);

				/* set the duration...*/
				number = atoi(argv[i]);
				if (number > 0)
					s->width = number;
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-wid requires a parameter\n");
			}

		}
		/* Handle the "hgt" parameter*/
		else if (strcmp(p, "hgt") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: hgt=\"%s\"", argv[i]);

				/* set the duration...*/
				number = atoi(argv[i]);
				if (number > 0)
					s->height = number;
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-hgt requires a parameter\n");
			}
		}
		/* Handle the "timeout" parameter*/
		else if (strcmp(p, "timeout") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: timeout=\"%s\"", argv[i]);
				/* set the duration...*/
				number = atoi(argv[i]);
				/* Add the timeout value (count of TIME_UNITS)
				 *  to struct,  TIME_UNIT is 1/8th of a second
				 */
				if (number > 0) {
					s->timeout = number;
					report(RPT_NOTICE, "Timeout set.");
				}
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-timeout requires a parameter\n");
			}
		}
		/* Handle the "backlight" parameter*/
		else if (strcmp(p, "backlight") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: backlight=\"%s\"", argv[i]);
				/* set the backlight status based on what the client has set*/
				switch(c->backlight) {
					case BACKLIGHT_OPEN:
						if (strcmp("on", argv[i]) == 0)
							s->backlight = BACKLIGHT_ON;

						if (strcmp("off", argv[i]) == 0)
							s->backlight = BACKLIGHT_OFF;

						if (strcmp("toggle", argv[i]) == 0) {
							if (s->backlight == BACKLIGHT_ON)
								s->backlight = BACKLIGHT_OFF;
							else if (s-backlight == BACKLIGHT_OFF)
								s->backlight = BACKLIGHT_ON;
						}

						if (strcmp("blink", argv[i]) == 0)
							s->backlight  |= BACKLIGHT_BLINK;

						if (strcmp("flash", argv[i]) == 0)
							s->backlight |= BACKLIGHT_FLASH;
					break;
					default:
						/*If the backlight is not OPEN then inherit its state*/
						s->backlight = c->backlight;
					break;
				}
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-backlight requires a parameter\n");
			}
		}
		/* Handle the "cursor" parameter */
		else if (strcmp(p, "cursor") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: cursor=\"%s\"", argv[i]);

				/* set the heartbeat type...*/
				if (0 == strcmp(argv[i], "off"))
					s->cursor = CURSOR_OFF;
				if (0 == strcmp(argv[i], "on"))
					s->cursor = CURSOR_DEFAULT_ON;
				if (0 == strcmp(argv[i], "under"))
					s->cursor = CURSOR_UNDER;
				if (0 == strcmp(argv[i], "block"))
					s->cursor = CURSOR_BLOCK;
				sock_send_string(c->sock, "success\n");
			}
			else {
				sock_send_error(c->sock, "-cursor requires a parameter\n");
			}
		}
		/* Handle the "cursor_x" parameter */
		else if (strcmp(p, "cursor_x") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: cursor_x=\"%s\"", argv[i]);

				/* set the position...*/
				number = atoi(argv[i]);
				if (number > 0 && number <= s->width) {
					s->cursor_x = number;
					sock_send_string(c->sock, "success\n");
				}
				else {
					sock_send_error(c->sock, "Cursor position outside screen\n");
				}
			}
			else {
				sock_send_error(c->sock, "-cursor_x requires a parameter\n");
			}
		}
		/* Handle the "cursor_y" parameter */
		else if (strcmp(p, "cursor_y") == 0) {
			if (argc > i + 1) {
				i++;
				debug(RPT_DEBUG, "screen_set: cursor_y=\"%s\"", argv[i]);

				/* set the position...*/
				number = atoi(argv[i]);
				if (number > 0 && number <= s->height) {
					s->cursor_y = number;
					sock_send_string(c->sock, "success\n");
				}
				else {
					sock_send_error(c->sock, "Cursor position outside screen\n");
				}
			}
			else {
				sock_send_error(c->sock, "-cursor_y requires a parameter\n");
			}
		}

		else sock_send_error(c->sock, "invalid parameter\n");
	}/* done checking argv*/
	return 0;
}