--- /dev/null	2012-08-31 05:09:01.000000000 +0400
+++ new/grubadm/menu.c	2012-08-31 05:09:01.141666449 +0400
@@ -0,0 +1,581 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2012, Daniil Lunev. All rights reserved.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "grubadm.h"
+#include "error.h"
+#include "menu.h"
+
+char default_entry[MAX_STRING_SIZE] = { 0 };
+char timeout[MAX_STRING_SIZE] = { 0 };
+char serial[MAX_STRING_SIZE] = { 0 };
+char terminal[MAX_STRING_SIZE] = { 0 };
+
+const char * cmd_list[] = {
+	"entry_name",
+	"pool_label",
+	"pool_uuid",
+	"data_set",
+	"kernel_path",
+	"kernel_options",
+	"module",
+	"default_entry",
+	"timeout",
+	"serial",
+	"terminal",
+	NULL
+};
+
+void
+free_menu (menu_list ** menu)
+{
+	unsigned int i;
+	menu_list * iter = *menu;
+	menu_list * tmp = *menu;
+
+	const char fnc[] = "free_menu";
+
+	debug_print ("%s\n", fnc);
+
+	while (iter) {
+		if (iter->entry.modules_amount)
+			for (i = 0; i < iter->entry.modules_amount; ++i)
+				free (iter->entry.modules[i]);
+		free (iter->entry.modules);
+		tmp = iter;
+		iter = iter->next;
+		free (tmp);
+	}
+
+	*menu = NULL;
+}
+
+static menu_list *
+new_entry (menu_list ** menu, const char * entry_name, const unsigned int num)
+{
+	menu_list * added_entry;
+	menu_list * iter;
+
+	const char fnc[] = "new_entry";
+
+	debug_print ("%s\nargs : %s | %u\n", fnc, entry_name, num);
+
+	added_entry = (menu_list *) calloc (1, sizeof (menu_list));
+	if (! added_entry) {
+		print_system_error ();
+		return NULL;
+	}
+
+	memset ((void *) added_entry, 0, sizeof (menu_list));
+	added_entry->entry_number = num;
+	strcpy (added_entry->entry.entry_name, entry_name);
+	added_entry->entry.modules = 
+		(char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
+
+	if (! added_entry->entry.modules) {
+		free_menu (&added_entry);
+		print_system_error ();
+		return NULL;
+	}
+
+	if (! *menu) {
+		*menu = added_entry;
+	} else {
+		for (iter = *menu; iter->next; iter = iter->next) 
+			if (! strcmp (iter->entry.entry_name, entry_name)) {
+				print_error ("%s %s", MSG_ERR_SAME_NAME, entry_name);
+				free_menu (&added_entry);
+				return NULL;
+			}
+		if (! strcmp (iter->entry.entry_name, entry_name)) {
+			print_error ("%s %s", MSG_ERR_SAME_NAME, entry_name);
+			free_menu (&added_entry);
+			return NULL;
+		}	
+		iter->next = added_entry;
+	}
+
+	return added_entry;
+}
+
+menu_list *
+add_entry (menu_list ** menu, menu_list * entry)
+{
+	menu_list * iter;
+
+	const char fnc[] = "add_entry";
+
+	debug_print ("%s\n", fnc);
+
+	if (! entry)
+		return NULL;
+
+	if (! *menu) {
+		entry->entry_number = 0;
+		*menu = entry;
+		return entry;
+	}
+
+	for (iter = * menu; iter->next; iter = iter->next)
+		if (! strcmp (iter->entry.entry_name, entry->entry.entry_name))
+			return NULL;
+		
+	if (! strcmp (iter->entry.entry_name, entry->entry.entry_name))
+		return NULL;
+
+	iter->next = entry;
+	entry->entry_number = iter->entry_number + 1;
+	return entry;
+}
+
+menu_list *
+clone_entry (menu_list * entry)
+{
+	unsigned int i;
+
+	char fnc[] = "clone_entry";
+
+	debug_print ("%s\n", fnc);
+
+	if (! entry)
+		return NULL;
+	menu_list * clone = (menu_list *) calloc (1, sizeof (menu_list));
+	memcpy ((void *) clone, (void *) entry, sizeof (menu_list));
+	clone->next = NULL;
+	clone->entry.modules = (char **) calloc (MAX_MODULE_AMOUNT, sizeof (char *));
+	for (i = 0; i < clone->entry.modules_amount; ++i)
+		clone->entry.modules[i] = strdup (entry->entry.modules[i]);
+	return clone;
+}
+
+menu_list * 
+enable_hyper (menu_list * entry)
+{
+	unsigned int i;
+	char kernel_mod[MAX_STRING_SIZE];
+
+	char fnc[] = "enable_hyper";
+
+	debug_print ("%s\n", fnc);
+
+	if (! entry)
+		return NULL;
+	
+	strcpy (kernel_mod, entry->entry.kernel);
+	strcat (kernel_mod, " ");
+	strcat (kernel_mod, entry->entry.kernel);
+	strcat (kernel_mod, " ");
+	strcat (kernel_mod, entry->entry.kernel_opts);
+
+	strcpy (entry->entry.kernel, HYPER_KERNEL);
+	strcpy (entry->entry.kernel_opts, HYPER_KERNEL_OPTS);
+	
+	for (i = entry->entry.modules_amount; i > 0; --i) 
+		entry->entry.modules[i] = entry->entry.modules[i-1];
+	entry->entry.modules[0] = strdup (kernel_mod);
+	++(entry->entry.modules_amount);
+
+	return entry;
+}
+
+menu_list *
+disable_hyper (menu_list * entry)
+{
+	unsigned int i;
+	char * tmp;
+
+	char fnc[] = "disable_hyper";
+
+	debug_print ("%s\n", fnc);
+
+	if (! entry)
+		return NULL;
+
+	tmp = strchr (entry->entry.modules[0], ' ');
+	if (! tmp)
+		return NULL;
+
+	*tmp++ = '\0';
+
+	strcpy (entry->entry.kernel, entry->entry.modules[0]);
+	
+	entry->entry.kernel_opts[0] = '\0';
+	tmp = strchr(tmp, ' ');
+	if (tmp && *(++tmp)) {
+		strcpy (entry->entry.kernel_opts, tmp);
+	}
+	free (entry->entry.modules[0]);
+	--(entry->entry.modules_amount);
+
+	for (i = 0; i < entry->entry.modules_amount; ++i)
+		entry->entry.modules[i] = entry->entry.modules[i + 1];
+
+	return entry;
+}
+
+int
+delete_entry (menu_list ** menu, menu_list * del)
+{
+	menu_list * iter = *menu;
+	int d_num, i_num;
+
+	const char fnc[] = "delete_entry";
+
+	debug_print ("%s\n", fnc);
+
+	if (! menu || ! del)
+		return 0;
+
+	i_num = del->entry_number;
+
+	if (iter == del) {
+		*menu = (*menu)->next;
+		iter->next = NULL;
+		free_menu (&iter);
+	} else {
+		while (iter->next) {
+			if (iter->next == del) {
+				iter->next = del->next;
+				del->next = NULL;
+				free_menu (&del);
+				break;
+			}
+			iter = iter->next;
+		}
+	}
+
+	sscanf (default_entry, "%d", &d_num);
+
+	if (i_num <= d_num) {
+		d_num = d_num ? d_num - 1 : 0;
+		sprintf (default_entry, "%d", d_num);
+	}
+
+	return 0;
+}
+
+static menu_list *
+add_param (menu_list ** menu, 
+		const cmd_id id,
+		const char * value, 
+		unsigned int * num)
+{
+	menu_list * iter;
+	char * tmp;
+
+	const char fnc[] = "add_param";
+
+	debug_print ("%s\narg: %u | %s | %u\n", fnc, (unsigned int) id, value, *num);
+
+	if (id == CMD_ENTRY_NAME)
+		return new_entry (menu, value, (*num)++);
+
+	if (! *menu) {
+		print_error ("%s, %s", MSG_ERR_WRONG_SYNTAX, "entry isn't defined");
+	}
+
+	for (iter = * menu; iter->next; iter = iter->next)
+		;
+
+	switch (id) {
+	case CMD_POOL_LABEL:
+		strcpy (iter->entry.pool_label, value);
+		break;
+	case CMD_POOL_UUID:
+		strcpy (iter->entry.pool_uuid, value);
+		break;
+	case CMD_DATA_SET:
+		strcpy (iter->entry.dataset, value);
+		break;
+	case CMD_KERNEL_PATH:
+		strcpy (iter->entry.kernel, value);
+		break;
+	case CMD_KERNEL_OPTIONS:
+		strcpy (iter->entry.kernel_opts, value);
+		break;
+	case CMD_BA_PATH:
+		if (iter->entry.modules_amount >= MAX_MODULE_AMOUNT)
+			return NULL; 
+		iter->entry.modules[iter->entry.modules_amount] = 
+			(char *) malloc (MAX_STRING_SIZE);
+		if (! iter->entry.modules[iter->entry.modules_amount])
+			return NULL;
+		strcpy (iter->entry.modules[iter->entry.modules_amount], value);
+		++(iter->entry.modules_amount);
+		break;
+	default:
+		return NULL;
+	}
+	return iter;
+}
+
+void
+list_menu (menu_list * menu)
+{
+	const char fnc[] = "list_menu";
+
+	debug_print ("%s\n", fnc);
+
+	if (*default_entry)
+		printf ("%s%s%s\n", cmd_list[CMD_DEFAULT], SEPARATOR, default_entry);
+
+	if (*timeout) 
+		printf ("%s%s%s\n", cmd_list[CMD_TIMEOUT], SEPARATOR, timeout);
+
+	if (*serial)
+		printf ("%s%s%s\n", cmd_list[CMD_SERIAL], SEPARATOR, serial);
+
+	if (*terminal)
+		printf ("%s%s%s\n", cmd_list[CMD_TERMINAL], SEPARATOR, terminal);
+
+	while (menu) {
+		printf("%u : %s\n", menu->entry_number, menu->entry.entry_name);
+		menu = menu->next;
+	}
+}
+
+menu_list *
+find_entry (menu_list * menu, menu_list * find)
+{
+	unsigned int i = 0;
+	const char fnc[] = "find_entry";
+
+	debug_print ("%s\n", fnc);
+
+	if (! menu || ! find)
+		return NULL;
+
+	while (menu) {
+		if (find->entry.entry_name[0])
+			if (strcmp (menu->entry.entry_name, find->entry.entry_name))
+				goto out;
+		if (find->entry.pool_label[0])
+			if (strcmp (menu->entry.pool_label, find->entry.pool_label))
+				goto out;
+		if (find->entry.pool_uuid[0])
+			if (strcmp (menu->entry.pool_uuid, find->entry.pool_uuid))
+				goto out;
+		if (find->entry.dataset[0])
+			if (strcmp (menu->entry.dataset, find->entry.dataset))
+				goto out;
+		if (find->entry.kernel[0])
+			if (strcmp (menu->entry.kernel, find->entry.kernel))
+				goto out;
+
+		if (find->entry.modules_amount) {
+			for (i = 0; i < menu->entry.modules_amount; ++i)
+				if (! strcmp (menu->entry.modules[i], find->entry.modules[0]))
+					return menu;
+			goto out;
+		}
+
+		return menu;
+out:
+		menu = menu->next;
+	}
+
+	return NULL;
+}
+
+menu_list *
+get_entry_by_number (menu_list * menu, int number)
+{
+	while (menu) 
+		if (menu->entry_number == number)
+			return menu;
+		else
+			menu = menu->next;
+	return NULL;
+}
+
+static size_t
+get_line (FILE * menu_file, char * line)
+{
+	const char fnc[] = "get_line";
+	
+	debug_print ("%s\n", fnc);
+
+	if (! fgets (line, MAX_STRING_SIZE, menu_file)) {
+		if (! feof (menu_file))
+			print_system_error ();
+		return -1;
+	}
+
+	if ((line[strlen (line) - 1] != '\n') && (! feof (menu_file))) {
+		print_error ("%s (> %d)", MSG_ERR_LONG_LINE, MAX_STRING_SIZE);
+		return -1;
+	}
+
+	line[strlen (line) - 1] = '\0';
+
+	return strlen (line);
+}
+
+static cmd_id
+get_param_id (const char * param)
+{
+	unsigned int id;
+
+	const char fnc[] = "get_param_id";
+
+	debug_print ("%s\narg : %s\n", fnc, param);
+
+	for (id = 0; cmd_list[id]; ++id)
+		if (! strcmp (cmd_list[id], param))
+			break;
+
+	return id;		
+}
+
+menu_list *
+parse_file (const char * file_name, int * entries_amount)
+{
+	FILE * menu_file = NULL;
+	unsigned int counter = 0;
+	menu_list * menu = NULL;
+	char line[MAX_STRING_SIZE];
+	char * param;
+	char * value;
+	cmd_id id;
+	
+	const char fnc[] = "parse_file";
+
+	debug_print ("%s\narg : %s\n", fnc, file_name);
+
+	menu_file = fopen (file_name, "r");
+	if (! menu_file) {
+		print_system_error ();
+		goto out;
+	}
+
+	for (;;) {
+		if (get_line (menu_file, line) == -1) {
+			if (feof (menu_file))
+				break;
+			free_menu (&menu);
+			counter = -1;
+			goto out;
+		}
+
+		if (line[0] == '#')
+			continue;
+		
+		param = line;
+		while ((*param == ' ') || (*param == '\t'))
+			++ param;
+		if (*param == '\0')
+			continue;
+
+		value = strchr (line, '=');
+		if (! value) {
+			print_error ("%s at line '%s'", MSG_ERR_WRONG_SYNTAX, line);
+			free_menu (&menu);
+			counter = -1;
+			goto out;
+		} 
+		*value++ = '\0';
+
+		id = get_param_id (param);
+		if (id == CMD_UNKNOWN) {
+			print_error ("%s at line '%s'", MSG_ERR_WRONG_SYNTAX, line);
+			free_menu (&menu);
+			counter = -1;
+			goto out;
+		}
+
+		if (id == CMD_DEFAULT) {
+			strcpy (default_entry, value);
+		} else if (id == CMD_TIMEOUT) {
+			strcpy (timeout, value);
+		} else if (id == CMD_SERIAL) {
+			strcpy (serial, value);
+		} else if (id == CMD_TERMINAL) {
+			strcpy (terminal, value);
+		} else if (! add_param (&menu, id, value, &counter)) {
+			free_menu (&menu);
+			counter = -1;
+			goto out;
+		}
+	}
+
+out:
+	if (menu_file)
+		fclose (menu_file);
+
+	*entries_amount = counter;
+
+	return menu;
+}
+
+int
+write_menu (const char * config, const char * tmp_config, menu_list * menu)
+{
+	FILE * tmp_menu = fopen (tmp_config, "w");
+	unsigned int i = 0;
+
+	const char fnc[] = "write_menu";
+
+	debug_print ("%s\n", fnc);
+
+	if (! tmp_menu) {
+		print_system_error();
+		return 0;
+	}
+	
+	if (default_entry[0])
+		fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_DEFAULT], SEPARATOR, default_entry);
+	if (timeout[0])
+		fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_TIMEOUT], SEPARATOR, timeout);
+	if (serial[0])
+		fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_SERIAL], SEPARATOR, serial);
+	if (terminal[0])
+		fprintf (tmp_menu, "%s%s%s\n\n", cmd_list[CMD_TERMINAL], SEPARATOR, terminal);
+
+	while (menu) {
+		fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_ENTRY_NAME], SEPARATOR, menu->entry.entry_name);
+		if (menu->entry.pool_label[0])
+			fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_POOL_LABEL], SEPARATOR, menu->entry.pool_label);
+		if (menu->entry.pool_uuid[0])
+			fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_POOL_UUID], SEPARATOR, menu->entry.pool_uuid);
+		if (menu->entry.dataset[0])
+			fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_DATA_SET], SEPARATOR, menu->entry.dataset);
+		if (menu->entry.kernel[0])
+			fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_KERNEL_PATH], SEPARATOR, menu->entry.kernel);
+		if (menu->entry.kernel_opts[0])
+			fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_KERNEL_OPTIONS], SEPARATOR, menu->entry.kernel_opts);
+		if (menu->entry.modules_amount)
+			for (i = 0; i < menu->entry.modules_amount; ++i)
+				fprintf (tmp_menu, "%s%s%s\n", cmd_list[CMD_BA_PATH], SEPARATOR, menu->entry.modules[i]);
+		fprintf (tmp_menu, "#----------------------------------------------------\n");
+		menu = menu->next;
+	}
+
+	fclose (tmp_menu);
+
+	remove (config);
+	rename (tmp_config, config);
+
+	return 1;
+}
