/***************************************************************************
mondo-rstr-newt.c  -  description
-----------------

begin: Fri Apr 19 16:40:35 EDT 2002
copyright : (C) 2002 Mondo  Hugo Rabson
email	  : Hugo Rabson <hugorabson@msn.com>
edited by : by Stan Benoit 4/2002
email     : troff@nakedsoul.org
cvsid     : $Id: mondo-rstr-newt.c 738 2006-08-03 21:17:09Z andree $
***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   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     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
/* mondo-rstr-newt.c               Hugo Rabson


07/12
- wider mountlist entry editor window

03/28/2004
- removed g_mountlist_fname

07/02/2003
- fixed calls to popup_and_get_string()

06/05
- cleaned up get_isodir_info()

05/02
- added strip_path() to improve file list display for long paths (Conor)

04/24
- added lots of assert()'s and log_OS_error()'s

04/08/2003
- commented out call to sort_mountlist_by_device(), just in case it's 
  futzing with LVM/RAID users

08/21/2002
- fix MOUNTLIST_FNAME and RAIDTAB_FNAME if Reload button finds them empty

08/19
- fixed what_number_cd_is_this() to take bkpinfo as parameter

07/01 - 07/31
- renamed from mondo-newt.c to mondo-rstr-newt.c
- moved lots of subroutines to libmondo-newt.c
- added hooks to libmondo
- better handling of yes/no/YES/NO
- better "% done" feedback (Philippe de Muyter)

05/01 - 06/30
- trim trailing \n from fgets() in a couple of functions
- expanded "May I write mountlist?" message
- replace scanf() with a properly restricted fgets() (Troff)

02/01 - 02/28/2002
- allow up to 50 chars in popup_and_get_string()'s input field
- if g_current_progress > g_maximum_progress then g_current_progress=g_max...
  to stop the progress bar from shooting off the RHS of the screen :)
- beefed up the RAID-related logging a bit
- don't complain if partition format is a number (1-255)
- if hard disk does not exist then warn user when editing mountlist
- when running 'eject -t', log its output

[...]

08/09/2001
- created
*/


/**
 * @file
 * Functions for handling GUI interfaces in the restore.
 */

#ifdef __FreeBSD__
#define OSSWAP(linux,fbsd) fbsd
//#include <libgen.h>
#else
#define OSSWAP(linux,fbsd) linux
#endif

#include "mondo-rstr-newt.h"

//static char cvsid[] = "$Id: mondo-rstr-newt.c 738 2006-08-03 21:17:09Z andree $";

extern char err_log_lines[NOOF_ERR_LINES][MAX_STR_LEN];


/**
 * @defgroup restoreGuiDisklist Disklist GUI
 * Functions for manipulating the disklist GUI.
 * @ingroup restoreGuiGroup
 */
/**
 * @defgroup restoreGuiMountlist Mountlist GUI
 * Functions for manipulating the mountlist/raidlist GUI.
 * @ingroup restoreGuiGroup
 */
/**
 * @defgroup restoreGuiVarslist RAID Variables GUI
 * Functions for manipulating the RAID variables GUI.
 * @ingroup restoreGuiGroup
 */

/**
 * @addtogroup restoreGuiGroup
 * @{
 */
/**
 * Add an entry in @p disklist from the list in @p unallocated_raid_partitions.
 * @param disklist The disklist to add an entry to.
 * @param raid_device Unused; make sure it's non-NULL non-"".
 * @param unallocated_raid_partitions The list of unallocated RAID partitions
 * that the user may choose from.
 * @bug raid_device is unused.
 * @ingroup restoreGuiDisklist
 */
void
add_disklist_entry(struct list_of_disks *disklist, char *raid_device,
				   struct mountlist_itself *unallocated_raid_partitions)
{
	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	/** newt **************************************************************/
	newtComponent myForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent b_res;
	newtComponent partitionsListbox;
	newtComponent headerMsg;

  /** prototypes *********************************************************/
	void *keylist[ARBITRARY_MAXIMUM];
	void *curr_choice;

	/** int ****************************************************************/
	int i = 0;
	int index = 0;
	int currline = 0;
	int items = 0;

	assert(disklist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(raid_device);
	assert(unallocated_raid_partitions != NULL);

	newtPushHelpLine
		("   Add one of the following unallocated RAID partitions to this RAID device.");
	sprintf(tmp, "%-26s %s", "Device", "Size");
	headerMsg = newtLabel(1, 1, tmp);
	partitionsListbox =
		newtListbox(1, 2, 6, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
	redraw_unallocpartnslist(unallocated_raid_partitions, keylist,
							 partitionsListbox);
	i = 7;
	bOK = newtCompactButton(i, 9, "  OK  ");
	bCancel = newtCompactButton(i += 9, 9, "Cancel");
	newtOpenWindow(22, 6, 36, 10, "Unallocated RAID partitions");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, headerMsg, partitionsListbox, bOK,
						  bCancel, NULL);
	b_res = newtRunForm(myForm);
	if (b_res != bCancel) {
		curr_choice = newtListboxGetCurrent(partitionsListbox);
		for (currline = 0;
			 currline < unallocated_raid_partitions->entries
			 && keylist[currline] != curr_choice; currline++);
		if (currline == unallocated_raid_partitions->entries
			&& unallocated_raid_partitions->entries > 0) {
			log_it("I don't know what this button does");
		} else {
			index = find_next_free_index_in_disklist(disklist);

			items = disklist->entries;
			strcpy(disklist->el[items].device,
				   unallocated_raid_partitions->el[currline].device);
			disklist->el[items].index = index;
			disklist->entries = ++items;

		}
	}
	newtFormDestroy(myForm);
	newtPopWindow();
	newtPopHelpLine();
}




/**
 * Add an entry to @p mountlist.
 * @param mountlist The mountlist to add an entry to.
 * @param raidlist The raidlist that accompanies @p mountlist.
 * @param listbox The listbox component in the mountlist editor.
 * @param currline The line selected in @p listbox.
 * @param keylist The list of keys for @p listbox.
 * @ingroup restoreGuiMountlist
 */
void
add_mountlist_entry(struct mountlist_itself *mountlist,
					struct raidlist_itself *raidlist,
					newtComponent listbox, int currline, void *keylist[])
{

	/** int **************************************************************/
	int i = 0;
	int num_to_add = 0;

	/** newt *************************************************************/
	newtComponent myForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent b_res;
	newtComponent mountpointComp;
	newtComponent label0;
	newtComponent label1;
	newtComponent label2;
	newtComponent label3;
	newtComponent sizeComp;
	newtComponent deviceComp;
	newtComponent formatComp;

	/** buffers **********************************************************/
	char drive_to_add[MAX_STR_LEN];
	char mountpoint_str[MAX_STR_LEN];
	char size_str[MAX_STR_LEN];
	char device_str[MAX_STR_LEN];
	char format_str[MAX_STR_LEN];

	/** pointers *********************************************************/
	char *mountpoint_here;
	char *size_here;
	char *device_here;
	char *format_here;

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert(listbox != NULL);
	assert(keylist != NULL);

	strcpy(device_str, "/dev/");
	strcpy(mountpoint_str, "/");
#ifdef __FreeBSD__
	strcpy(format_str, "ufs");
#else
	strcpy(format_str, "ext2");
#endif
	size_str[0] = '\0';
	/* sprintf(size_str,""); */
	newtOpenWindow(20, 5, 48, 10, "Add entry");
	label0 = newtLabel(2, 1, "Device:    ");
	label1 = newtLabel(2, 2, "Mountpoint:");
	label2 = newtLabel(2, 3, "Size (MB): ");
	label3 = newtLabel(2, 4, "Format:    ");
	deviceComp =
		newtEntry(14, 1, device_str, 30, (void *) &device_here, 0);
	mountpointComp =
		newtEntry(14, 2, mountpoint_str, 30, (void *) &mountpoint_here, 0);
	formatComp =
		newtEntry(14, 4, format_str, 15, (void *) &format_here, 0);
	sizeComp = newtEntry(14, 3, size_str, 10, (void *) &size_here, 0);
	bOK = newtButton(5, 6, "  OK  ");
	bCancel = newtButton(17, 6, "Cancel");
	newtPushHelpLine
		("To add an entry to the mountlist, please fill in these fields and then hit 'OK'");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, deviceComp, mountpointComp, sizeComp,
						  formatComp, label0, label1, label2, label3, bOK,
						  bCancel, NULL);
	for (b_res = NULL; b_res != bOK && b_res != bCancel;) {
		b_res = newtRunForm(myForm);
		strcpy(device_str, device_here);
		strcpy(mountpoint_str, mountpoint_here);
		strcpy(format_str, format_here);
		strcpy(size_str, size_here);
//      log_it ("Originals = %s,%s,%s,%s", device_str, mountpoint_str, format_str, size_str);
		strip_spaces(device_str);
		strip_spaces(mountpoint_str);
		strip_spaces(format_str);
		strip_spaces(size_str);
//      log_it ("Modified = %s,%s,%s,%s", device_str, mountpoint_str, format_str, size_str);
		if (b_res == bOK) {
			if (device_str[strlen(device_str) - 1] == '/') {
				popup_and_OK("You left the device nearly blank!");
				b_res = NULL;
			}
			if (size_of_specific_device_in_mountlist(mountlist, device_str)
				>= 0) {
				popup_and_OK("Can't add this - you've got one already!");
				b_res = NULL;
			}
		}
	}
	newtFormDestroy(myForm);
	newtPopHelpLine();
	newtPopWindow();
	if (b_res == bCancel) {
		return;
	}
	strcpy(drive_to_add, device_str);
	for (i = strlen(drive_to_add); isdigit(drive_to_add[i - 1]); i--);
	num_to_add = atoi(drive_to_add + i);
	drive_to_add[i] = '\0';
	currline = mountlist->entries;
	strcpy(mountlist->el[currline].device, device_str);
	strcpy(mountlist->el[currline].mountpoint, mountpoint_str);
	strcpy(mountlist->el[currline].format, format_str);
	mountlist->el[currline].size = atol(size_str) * 1024;
	mountlist->entries++;
	if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)) {
		initiate_new_raidlist_entry(raidlist, mountlist, currline,
									device_str);
	}
	redraw_mountlist(mountlist, keylist, listbox);
}


#ifndef __FreeBSD__
/**
 * Add an entry to the additional RAID variables section of @p raidrec.
 * @param raidrec The RAID device record containing the RAID variables list to add to.
 * @ingroup restoreGuiVarslist
 */
void add_varslist_entry(struct raid_device_record *raidrec)
{

	/** buffers ***********************************************************/
	char sz_out[MAX_STR_LEN];

	/** int ****************************************************************/
	int items = 0;
	int i = 0;

	assert(raidrec != NULL);

	sz_out[0] = '\0';
	if (popup_and_get_string
		("Add variable", "Enter the name of the variable to add", sz_out,
		 MAX_STR_LEN)) {
		strip_spaces(sz_out);
		items = raidrec->additional_vars.entries;
		for (i = 0;
			 i < items
			 && strcmp(raidrec->additional_vars.el[i].label, sz_out); i++);
		if (i < items) {
			popup_and_OK
				("No need to add that variable. It is already listed here.");
		} else {
			strcpy(raidrec->additional_vars.el[items].label, sz_out);
			edit_varslist_entry(raidrec, items);
			raidrec->additional_vars.entries = ++items;
		}
	}
}
#endif

/**
 * Calculate the size of @p raid_device.
 * @param mountlist The mountlist containing information about the user's partitions.
 * @param raidlist The raidlist that goes with @p mountlist.
 * @param raid_device The device to calculate the size of.
 * @return The size of the RAID device in Kilobytes.
 * @ingroup restoreUtilityGroup
 */
long
calculate_raid_device_size(struct mountlist_itself *mountlist,
						   struct raidlist_itself *raidlist,
						   char *raid_device)
{
#ifdef __FreeBSD__
	/** FreeBSD-specific version of calculate_raid_device_size() **/

	/** structures ********************************************************/
	struct vinum_volume *raidrec;

	/** int ***************************************************************/
	int i = 0, j = 0;
	int noof_partitions = 0;

	/** long **************************************************************/
	long total_size = 0;
	long plex_size = 0;
	long smallest_partition = 999999999;
	long smallest_plex = 999999999;
	long sp = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];




	for (i = 0;
		 i < raidlist->entries
		 && strcmp(raidlist->el[i].volname, basename(raid_device)); i++);
	if (i == raidlist->entries) {
		sprintf(tmp,
				"Cannot calc size of raid device %s - cannot find it in raidlist",
				raid_device);
		log_it(tmp);
		return (0);				// Isn't this more sensible than 999999999? If the raid dev !exists,
		// then it has no size, right?
	}
	raidrec = &raidlist->el[i];
	total_size = 0;
	if (raidrec->plexes == 0)
		return 0;
	for (j = 0; j < raidrec->plexes; j++) {
		plex_size = 0;
		int k = 0, l = 0;
		for (k = 0; k < raidrec->plex[j].subdisks; ++k) {
			char devname[64];
			strcpy(devname, raidrec->plex[j].sd[k].which_device);
			for (l = 0; l < raidlist->disks.entries; ++l) {
				if (!strcmp(devname, raidlist->disks.el[l].name)) {
					switch (raidrec->plex[j].raidlevel) {
					case -1:
						plex_size +=
							size_of_specific_device_in_mountlist(mountlist,
																 raidlist->
																 disks.
																 el[l].
																 device);
						break;
					case 0:
					case 5:
						if (size_of_specific_device_in_mountlist(mountlist,
																 raidlist->
																 disks.
																 el[l].
																 device) <
							smallest_partition) {
							smallest_partition =
								size_of_specific_device_in_mountlist
								(mountlist, raidlist->disks.el[l].device);
						}
						break;
					}
				}
			}
		}

		if (!is_this_raid_personality_registered
			(raidrec->plex[j].raidlevel)) {
			log_it
				("%s has a really weird RAID level - couldn't calc size :(",
				 raid_device);
			return (999999999);
		}
		if (raidrec->plex[j].raidlevel != -1) {
			plex_size = smallest_partition * (raidrec->plex[j].subdisks -
											  (raidrec->plex[j].
											   raidlevel == 5 ? 1 : 0));
		}
		if (plex_size < smallest_plex)
			smallest_plex = plex_size;

		smallest_partition = 999999999;
	}

	sprintf(tmp, "I have calculated %s's real size to be %ld", raid_device,
			(long) smallest_plex);
	log_it(tmp);
	return (smallest_plex);
#else
	/** Linux-specific version of calculate_raid_device_size() **/

	/** structures ********************************************************/
	struct raid_device_record *raidrec;

	/** int ***************************************************************/
	int i = 0;
	int noof_partitions = 0;

	/** long **************************************************************/
	long total_size = 0;
	long smallest_partition = 999999999;
	long sp = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(raid_device);

	for (i = 0;
		 i < raidlist->entries
		 && strcmp(raidlist->el[i].raid_device, raid_device); i++);
	if (i == raidlist->entries) {
		sprintf(tmp,
				"Cannot calc size of raid device %s - cannot find it in raidlist",
				raid_device);
		log_it(tmp);
		return (999999999);
	}
	raidrec = &raidlist->el[i];
	noof_partitions = raidrec->data_disks.entries;
	if (raidrec->raid_level == -1 || raidrec->raid_level == 0) {
		for (total_size = 0, i = 0; i < noof_partitions; i++) {
			total_size +=
				size_of_specific_device_in_mountlist(mountlist,
													 raidrec->data_disks.
													 el[i].device);
		}
	} else {
		for (i = 0; i < noof_partitions; i++) {
			sp = size_of_specific_device_in_mountlist(mountlist,
													  raidrec->data_disks.
													  el[i].device);
			if (smallest_partition > sp) {
				smallest_partition = sp;
			}
		}
		total_size = smallest_partition * (noof_partitions - 1);
	}
	sprintf(tmp, "I have calculated %s's real size to be %ld", raid_device,
			(long) total_size);
	log_it(tmp);
	return (total_size);
#endif
}



/**
 * Choose the RAID level for the RAID device record in @p raidrec.
 * @param raidrec The RAID device record to set the RAID level of.
 * @ingroup restoreGuiMountlist
 */
void
choose_raid_level(struct OSSWAP (raid_device_record, vinum_plex) * raidrec)
{

#ifdef __FreeBSD__

	/** int ***************************************************************/
	int res = 0;
	int out = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];
	char prompt[MAX_STR_LEN];
	char sz[MAX_STR_LEN];

	sprintf(prompt,
			"Please enter the RAID level you want. (concat, striped, raid5)");
	if (raidrec->raidlevel == -1) {
		strcpy(tmp, "concat");
	} else if (raidrec->raidlevel == 0) {
		strcpy(tmp, "striped");
	} else {
		sprintf(tmp, "raid%i", raidrec->raidlevel);
	}
	for (out = 999; out == 999;) {
		res = popup_and_get_string("Specify RAID level", prompt, tmp, 10);
		if (!res) {
			return;
		}
		strip_spaces(tmp);
		if (tmp[0] == '[' && tmp[strlen(tmp) - 1] == ']') {
			strcpy(sz, tmp);
			strncpy(tmp, sz + 1, strlen(sz) - 2);
			tmp[strlen(sz) - 2] = '\0';
		}
		if (!strcmp(tmp, "concat")) {
			out = -1;
		} else if (!strcmp(tmp, "striped")) {
			out = 0;
		} else if (!strcmp(tmp, "raid5")) {
			out = 5;
		}
		log_it(tmp);
		if (is_this_raid_personality_registered(out)) {
			log_it
				("Groovy. You've picked a RAID personality which is registered.");
		} else {
			if (ask_me_yes_or_no
				("You have chosen a RAID personality which is not registered with the kernel. Make another selection?"))
			{
				out = 999;
			}
		}
	}
	raidrec->raidlevel = out;
#else
	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];
	char personalities[MAX_STR_LEN];
	char prompt[MAX_STR_LEN];
	char sz[MAX_STR_LEN];
	int out = 0, res = 0;


	assert(raidrec != NULL);
	system
		("grep Pers /proc/mdstat > /tmp/raid-personalities.txt 2> /dev/null");
	strcpy(personalities,
		   last_line_of_file("/tmp/raid-personalities.txt"));
	sprintf(prompt, "Please enter the RAID level you want. %s",
			personalities);
	if (raidrec->raid_level == -1) {
		strcpy(tmp, "linear");
	} else {
		sprintf(tmp, "%d", raidrec->raid_level);
	}
	for (out = 999;
		 out != -1 && out != 0 && out != 1 && out != 4 && out != 5
		 && out != 10;) {
		res = popup_and_get_string("Specify RAID level", prompt, tmp, 10);
		if (!res) {
			return;
		}
		strip_spaces(tmp);
		if (tmp[0] == '[' && tmp[strlen(tmp) - 1] == ']') {
			strcpy(sz, tmp);
			strncpy(tmp, sz + 1, strlen(sz) - 2);
			tmp[strlen(sz) - 2] = '\0';
		}
		if (!strcmp(tmp, "linear")) {
			out = -1;
		} else if (!strncmp(tmp, "raid", 4)) {
			out = atoi(tmp + 4);
		} else {
			out = atoi(tmp);
		}
		log_it(tmp);
		if (is_this_raid_personality_registered(out)) {
			log_it
				("Groovy. You've picked a RAID personality which is registered.");
		} else {
			if (ask_me_yes_or_no
				("You have chosen a RAID personality which is not registered with the kernel. Make another selection?"))
			{
				out = 999;
			}
		}
	}
	raidrec->raid_level = out;
#endif
}



/**
 * Delete the partitions in @p disklist from @p mountlist because they
 * were part of a deleted RAID device.
 * @param mountlist The mountlist containing information about the user's partitions.
 * @param raidlist The raidlist that goes with @p mounntlist.
 * @param disklist The list of disks to remove from @p mountlist.
 * @ingroup restoreGuiDisklist
 */
void
del_partns_listed_in_disklist(struct mountlist_itself *mountlist,
							  struct raidlist_itself *raidlist,
							  struct list_of_disks *disklist)
{

	/** int ***************************************************************/
	int i = 0;
	int pos = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert(disklist != NULL);

	for (i = 0; i < disklist->entries; i++) {
		for (pos = 0;
			 pos < mountlist->entries
			 && strcmp(mountlist->el[pos].device, disklist->el[i].device);
			 pos++);
		if (pos < mountlist->entries) {
			sprintf(tmp,
					"Deleting partition %s cos it was part of a now-defunct RAID",
					mountlist->el[pos].device);
			log_it(tmp);
			memcpy((void *) &mountlist->el[pos],
				   (void *) &mountlist->el[mountlist->entries - 1],
				   sizeof(struct mountlist_line));
			mountlist->entries--;
		}
	}
}



/**
 * Delete entry number @p currline from @p disklist.
 * @param disklist The disklist to remove the entry from.
 * @param raid_device The RAID device containing the partition we're removing.
 * Used only in the popup "are you sure?" box.
 * @param currline The line number (starting from 0) of the item to delete.
 * @ingroup restoreGuiDisklist
 */
void
delete_disklist_entry(struct list_of_disks *disklist, char *raid_device,
					  int currline)
{

	/** int ***************************************************************/
	int pos = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	assert(disklist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(raid_device);

	sprintf(tmp, "Delete %s from RAID device %s - are you sure?",
			disklist->el[currline].device, raid_device);
	if (!ask_me_yes_or_no(tmp)) {
		return;
	}
	for (pos = currline; pos < disklist->entries - 1; pos++) {
		/* memcpy((void*)&disklist->el[pos], (void*)&disklist->el[pos+1], sizeof(struct s_disk)); */
		strcpy(disklist->el[pos].device, disklist->el[pos + 1].device);
	}
	disklist->entries--;
}



/**
 * Delete entry number @p currline from @p mountlist.
 * @param mountlist The mountlist to delete the entry from.
 * @param raidlist The raidlist that goes with @p mountlist.
 * @param listbox The Newt listbox component in the mountlist editor.
 * @param currline The line number (starting from 0) of the item to delete.
 * @param keylist The list of keys for @p listbox.
 * @ingroup restoreGuiMountlist
 */
void
delete_mountlist_entry(struct mountlist_itself *mountlist,
					   struct raidlist_itself *raidlist,
					   newtComponent listbox, int currline,
					   void *keylist[])
{

	/** int ***************************************************************/
	int pos = 0;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];
	char device[MAX_STR_LEN];


	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert(listbox != NULL);
	assert(keylist != NULL);

	pos =
		which_raid_device_is_using_this_partition(raidlist,
												  mountlist->el[currline].
												  device);
	if (pos >= 0) {
		sprintf(tmp, "Cannot delete %s: it is in use by RAID device %s",
				mountlist->el[currline].device,
				raidlist->el[pos].OSSWAP(raid_device, volname));
		popup_and_OK(tmp);
		return;
	}
	sprintf(tmp, "Delete %s - are you sure?",
			mountlist->el[currline].device);
	if (!ask_me_yes_or_no(tmp)) {
		return;
	}
	if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)) {
		strcpy(device, mountlist->el[currline].device);
		delete_raidlist_entry(mountlist, raidlist, device);
		for (currline = 0;
			 currline < mountlist->entries
			 && strcmp(mountlist->el[currline].device, device);
			 currline++);
		if (currline == mountlist->entries) {
			log_it("Dev is gone. I can't delete it. Ho-hum");
			return;
		}
	}
	memcpy((void *) &mountlist->el[currline],
		   (void *) &mountlist->el[mountlist->entries - 1],
		   sizeof(struct mountlist_line));
	mountlist->entries--;
	redraw_mountlist(mountlist, keylist, listbox);
}


/**
 * Delete @p device from @p raidlist.
 * @param mountlist The mountlist containing information about the user's partitions.
 * @param raidlist The raidlist containing the RAID device to delete.
 * @param device The device (e.g. /dev/md0) to delete.
 * @ingroup restoreGuiMountlist
 */
void
delete_raidlist_entry(struct mountlist_itself *mountlist,
					  struct raidlist_itself *raidlist, char *device)
{

	/** int ***************************************************************/
	int i = 0;
	int items = 0;

	/** bool **************************************************************/
	bool delete_partitions_too;

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(device);

	i = find_raid_device_in_raidlist(raidlist, device);
	if (i < 0) {
		return;
	}
	sprintf(tmp, "Do you want me to delete %s's partitions, too?", device);
	delete_partitions_too = ask_me_yes_or_no(tmp);
	if (delete_partitions_too) {
#ifdef __FreeBSD__
		// static so it's zeroed
		static struct list_of_disks d;
		int x, y, z;

		for (x = 0; x < raidlist->el[i].plexes; ++x) {
			for (y = 0; y < raidlist->el[i].plex[x].subdisks; ++y) {
				for (z = 0; z < raidlist->disks.entries; ++z) {
					if (!strcmp(raidlist->el[i].plex[x].sd[y].which_device,
								raidlist->disks.el[z].name)) {
						strcpy(d.el[d.entries].name,
							   raidlist->disks.el[z].name);
						strcpy(d.el[d.entries++].device,
							   raidlist->disks.el[z].device);
					}
				}
			}
		}

		del_partns_listed_in_disklist(mountlist, raidlist, &d);
#else
		del_partns_listed_in_disklist(mountlist, raidlist,
									  &raidlist->el[i].data_disks);
		del_partns_listed_in_disklist(mountlist, raidlist,
									  &raidlist->el[i].spare_disks);
		del_partns_listed_in_disklist(mountlist, raidlist,
									  &raidlist->el[i].parity_disks);
		del_partns_listed_in_disklist(mountlist, raidlist,
									  &raidlist->el[i].failed_disks);
#endif
	}
	items = raidlist->entries;
	if (items == 1) {
		items = 0;
	} else {
		log_it(tmp);
		memcpy((void *) &raidlist->el[i],
			   (void *) &raidlist->el[items - 1],
			   sizeof(struct OSSWAP (raid_device_record, vinum_volume)));
		items--;
	}
	raidlist->entries = items;
}


#ifndef __FreeBSD__
/**
 * Delete entry number @p lino in the additional RAID variables section of @p raidrec.
 * @param raidrec The RAID device record containing the RAID variable to delete.
 * @param lino The line number (starting from 0) of the variable to delete.
 * @ingroup restoreGuiVarslist
 */
void delete_varslist_entry(struct raid_device_record *raidrec, int lino)
{

	/** buffers ************************************************************/
	char tmp[MAX_STR_LEN];

	/** structures *********************************************************/
	struct additional_raid_variables *av;

	assert(raidrec != NULL);

	av = &raidrec->additional_vars;
	sprintf(tmp, "Delete %s - are you sure?", av->el[lino].label);
	if (ask_me_yes_or_no(tmp)) {
		if (!strcmp(av->el[lino].label, "persistent-superblock")
			|| !strcmp(av->el[lino].label, "chunk-size")) {
			sprintf(tmp, "%s must not be deleted. It would be bad.",
					av->el[lino].label);
			popup_and_OK(tmp);
		} else {
			memcpy((void *) &av->el[lino], (void *) &av->el[av->entries--],
				   sizeof(struct raid_var_line));
		}
	}
}
#endif


/**
 * Redraw the filelist display.
 * @param filelist The filelist structure to edit.
 * @param keylist The list of keys for @p listbox.
 * @param listbox The Newt listbox component containing some of the filelist entries.
 * @return The number of lines currently being displayed.
 * @ingroup restoreGuiGroup
 */
int
redraw_filelist(struct s_node *filelist, void *keylist[ARBITRARY_MAXIMUM],
				newtComponent listbox)
{

	/** int ***************************************************************/
	static int lines_in_flist_window = 0;
	static int depth = 0;

	/** long **************************************************************/
	long i = 0;

	/** structures *******************************************************/
	struct s_node *node;

	/** buffers **********************************************************/
	static char current_filename[MAX_STR_LEN];
	char tmp[MAX_STR_LEN + 2];

	/** bool *************************************************************/
	/*  void*dummyptr; */
	bool dummybool;
	static bool warned_already;

	assert(filelist != NULL);
	assert(keylist != NULL);
	assert(listbox != NULL);


	if (depth == 0) {
		lines_in_flist_window = 0;
		warned_already = FALSE;
		for (i = 0; i < ARBITRARY_MAXIMUM; i++) {
			g_strings_of_flist_window[i][0] = '\0';
			g_is_path_selected[i] = FALSE;
		}
	}
	for (node = filelist; node != NULL; node = node->right) {
		current_filename[depth] = node->ch;
		if (node->down) {
			depth++;
			i = redraw_filelist(node->down, keylist, listbox);
			depth--;
		}
		if (node->ch == '\0' && node->expanded) {
			if (lines_in_flist_window == ARBITRARY_MAXIMUM) {
				if (!warned_already) {
					warned_already = TRUE;
					sprintf(tmp,
							"Too many lines. Displaying first %d entries only. Close a directory to see more.",
							ARBITRARY_MAXIMUM);
					popup_and_OK(tmp);
				}
			} else {
				strcpy(g_strings_of_flist_window[lines_in_flist_window],
					   current_filename);
				g_is_path_selected[lines_in_flist_window] = node->selected;
				lines_in_flist_window++;
			}
		}
	}
	if (depth == 0) {
		if (lines_in_flist_window > ARBITRARY_MAXIMUM) {
			lines_in_flist_window = ARBITRARY_MAXIMUM;
		}
/* do an elementary sort */
		for (i = 1; i < lines_in_flist_window; i++) {
			if (strcmp
				(g_strings_of_flist_window[i],
				 g_strings_of_flist_window[i - 1]) < 0) {
				strcpy(tmp, g_strings_of_flist_window[i]);
				strcpy(g_strings_of_flist_window[i],
					   g_strings_of_flist_window[i - 1]);
				strcpy(g_strings_of_flist_window[i - 1], tmp);
				dummybool = g_is_path_selected[i];
				g_is_path_selected[i] = g_is_path_selected[i - 1];
				g_is_path_selected[i - 1] = dummybool;
				i = 0;
			}
		}
/* write list to screen */
		newtListboxClear(listbox);
		for (i = 0; i < lines_in_flist_window; i++) {
			sprintf(tmp, "%c%c %-80s", (g_is_path_selected[i] ? '*' : ' '),
					(g_is_path_expanded[i] ? '+' : '-'),
					strip_path(g_strings_of_flist_window[i]));
			tmp[70] = '\0';
			keylist[i] = (void *) i;
			newtListboxAppendEntry(listbox, tmp, keylist[i]);
		}
		return (lines_in_flist_window);
	} else {
		return (0);
	}
}


/**
 * Strip a path to the bare minimum (^ pointing to the directory above, plus filename).
 * @param tmp The path to strip.
 * @return The stripped path.
 * @author Conor Daly
 * @ingroup restoreUtilityGroup
 */
char *strip_path(char *tmp)
{

	int i = 0, j = 0, slashcount = 0;
	int slashloc = 0, lastslashloc = 0;

	while (tmp[i] != '\0') {	/* Count the slashes in tmp
								   1 slash per dir */
		if (tmp[i] == '/') {
			slashcount++;
			lastslashloc = slashloc;
			slashloc = i;
			if (tmp[i + 1] == '\0') {	/* if this slash is last char, back off */
				slashcount--;
				slashloc = lastslashloc;
			}
		}
		i++;
	}
	if (slashcount > 0)
		slashcount--;			/* Keep one slash 'cos Hugh does... */

	for (i = 0; i < slashcount; i++) {	/* Replace each dir with a space char */
		tmpnopath[i] = ' ';
	}

	i = slashloc;
	j = slashcount;
	while (tmp[i] != '\0') {	/* Now add what's left of tmp */
		if ((tmpprevpath[j] == ' ' || tmpprevpath[j] == '^')
			&& tmp[i] == '/' && tmpnopath[j - 1] != '^' && j != 0) {	/* Add a pointer upwards if this is not in the same dir as line above */
			tmpnopath[j - 1] = '^';
		} else {
			tmpnopath[j++] = tmp[i++];
		}
	}
	tmpnopath[j] = '\0';

	strcpy(tmpprevpath, tmpnopath);	/* Make a copy for next iteration */

	return (tmpnopath);
}


/**
 * Allow the user to edit the filelist and choose which files to restore.
 * @param filelist The node structure containing the filelist.
 * @return 0 if the user pressed OK, 1 if they pressed Cancel.
 */
int edit_filelist(struct s_node *filelist)
{

	/** newt **************************************************************/
	newtComponent myForm;
	newtComponent bLess = NULL;
	newtComponent bMore = NULL;
	newtComponent bToggle = NULL;
	newtComponent bOK = NULL;
	newtComponent bCancel = NULL;
	newtComponent b_res = NULL;
	newtComponent filelistListbox = NULL;
	newtComponent bRegex = NULL;

	/** int ***************************************************************/
	int finished = FALSE;
	int lines_in_flist_window = 0;
	int indexno = 0;
	int j = 0;

	/** ???? **************************************************************/
	void *curr_choice;
	void *keylist[ARBITRARY_MAXIMUM];

	/** buffers ***********************************************************/
	char tmp[MAX_STR_LEN];

	/** bool **************************************************************/
	bool dummybool;

/*  struct s_node *node; */

	assert(filelist != NULL);

	log_to_screen("Editing filelist");
	newtPushHelpLine
		("   Please edit the filelist to your satisfaction, then click OK or Cancel.");
	j = 4;
	bLess = newtCompactButton(j, 17, " Less ");
	bMore = newtCompactButton(j += 12, 17, " More ");
	bToggle = newtCompactButton(j += 12, 17, "Toggle");
	bRegex = newtCompactButton(j += 12, 17, "RegEx");
	bCancel = newtCompactButton(j += 12, 17, "Cancel");
	bOK = newtCompactButton(j += 12, 17, "  OK  ");
	filelistListbox =
		newtListbox(2, 1, 15, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
	toggle_all_root_dirs_on(filelist);
	lines_in_flist_window =
		redraw_filelist(filelist, keylist, filelistListbox);
	newtOpenWindow(1, 3, 77, 18, "Editing filelist");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, filelistListbox, bLess, bMore, bToggle,
						  bRegex, bCancel, bOK, NULL);
	while (!finished) {
		b_res = newtRunForm(myForm);
		if (b_res == bOK) {
			finished =
				ask_me_yes_or_no
				("Are you happy with your file selection?");
		} else if (b_res == bCancel) {
			finished = TRUE;
		} else if (b_res == bRegex) {
			popup_and_OK("I haven't implemented this yet...");
		} else {
			curr_choice = newtListboxGetCurrent(filelistListbox);
			for (indexno = 0;
				 indexno < lines_in_flist_window
				 && keylist[indexno] != curr_choice; indexno++);
			if (indexno == lines_in_flist_window) {
				log_it
					("I don't know what this button does; assuming I am to toggle 1st entry");
				indexno = 0;
			}
			sprintf(tmp, "You selected '%s'",
					g_strings_of_flist_window[indexno]);
			log_it(tmp);
			if (b_res == bMore) {
				g_is_path_expanded[indexno] = TRUE;
				toggle_path_expandability(filelist,
										  g_strings_of_flist_window
										  [indexno], TRUE);
				lines_in_flist_window =
					redraw_filelist(filelist, keylist, filelistListbox);
				newtListboxSetCurrentByKey(filelistListbox, curr_choice);
			} else if (b_res == bLess) {
				g_is_path_expanded[indexno] = FALSE;
				toggle_path_expandability(filelist,
										  g_strings_of_flist_window
										  [indexno], FALSE);
				lines_in_flist_window =
					redraw_filelist(filelist, keylist, filelistListbox);
				newtListboxSetCurrentByKey(filelistListbox, curr_choice);
			} else {
				if (!strcmp(g_strings_of_flist_window[indexno], "/")) {
					dummybool = !g_is_path_selected[indexno];
					for (j = 1; j < lines_in_flist_window; j++) {
						toggle_path_selection(filelist,
											  g_strings_of_flist_window[j],
											  dummybool);
					}
				} else {
					toggle_path_selection(filelist,
										  g_strings_of_flist_window
										  [indexno],
										  !g_is_path_selected[indexno]);
					lines_in_flist_window =
						redraw_filelist(filelist, keylist,
										filelistListbox);
				}
				newtListboxSetCurrentByKey(filelistListbox, curr_choice);
			}
			for (indexno = 0;
				 indexno < lines_in_flist_window
				 && keylist[indexno] != curr_choice; indexno++);
			if (indexno == lines_in_flist_window) {
				log_it
					("Layout of table has changed. Y pointer is reverting to zero.");
				indexno = 0;
			}
		}
	}
	newtFormDestroy(myForm);
	newtPopWindow();
	newtPopHelpLine();
	if (b_res == bOK) {
		return (0);
	} else {
/*    popup_and_OK("You pushed 'cancel'. I shall now abort."); */
		return (1);
	}
}


/**
 * Edit an entry in @p mountlist.
 * @param mountlist The mountlist containing information about the user's partitions.
 * @param raidlist The raidlist to accompany @p mountlist.
 * @param listbox The Newt listbox component in the mountlist editor.
 * @param currline The selected line (starting from 0) in @p listbox.
 * @param keylist The list of keys for @p listbox.
 * @ingroup restoreGuiMountlist
 */
void
edit_mountlist_entry(struct mountlist_itself *mountlist,
					 struct raidlist_itself *raidlist,
					 newtComponent listbox, int currline, void *keylist[])
{

	/** structures ********************************************************/
	static struct raidlist_itself bkp_raidlist;

	/** newt **************************************************************/
	newtComponent myForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent b_res;
	newtComponent mountpointComp;
	newtComponent label0;
	newtComponent label1;
	newtComponent label2;
	newtComponent label3;
	newtComponent sizeComp;
	newtComponent deviceComp;
	newtComponent formatComp;
	newtComponent b_raid = NULL;

  /** buffers ***********************************************************/
	char device_str[MAX_STR_LEN];
	char mountpoint_str[MAX_STR_LEN];
	char size_str[MAX_STR_LEN];
	char format_str[MAX_STR_LEN];
	char tmp[MAX_STR_LEN];
	char device_used_to_be[MAX_STR_LEN];
	char mountpt_used_to_be[MAX_STR_LEN];

	/** pointers **********************************************************/
	char *device_here;
	char *mountpoint_here;
	char *size_here;
	char *format_here;

	/** int ***************************************************************/
	int j = 0;

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert(listbox != NULL);
	assert(keylist != NULL);

	memcpy((void *) &bkp_raidlist, (void *) raidlist,
		   sizeof(struct raidlist_itself));
	strcpy(device_str, mountlist->el[currline].device);
	strcpy(device_used_to_be, mountlist->el[currline].device);
	strcpy(mountpoint_str, mountlist->el[currline].mountpoint);
	strcpy(mountpt_used_to_be, mountlist->el[currline].mountpoint);
	strcpy(format_str, mountlist->el[currline].format);
	sprintf(size_str, "%lld", mountlist->el[currline].size / 1024);
	newtOpenWindow(20, 5, 48, 10, "Edit entry");
	label0 = newtLabel(2, 1, "Device:");
	label1 = newtLabel(2, 2, "Mountpoint:");
	label2 = newtLabel(2, 3, "Size (MB): ");
	label3 = newtLabel(2, 4, "Format:    ");
	deviceComp =
		newtEntry(14, 1, device_str, 30, (void *) &device_here, 0);
	mountpointComp =
		newtEntry(14, 2, mountpoint_str, 30, (void *) &mountpoint_here, 0);
	formatComp =
		newtEntry(14, 4, format_str, 15, (void *) &format_here, 0);
	if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)
		|| !strcmp(mountlist->el[currline].mountpoint, "image")) {
		sizeComp = newtLabel(14, 3, size_str);
	} else {
		sizeComp = newtEntry(14, 3, size_str, 10, (void *) &size_here, 0);
	}
	bOK = newtButton(2, 6, "  OK  ");
	bCancel = newtButton(14, 6, "Cancel");
	if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)) {
		b_raid = newtButton(26, 6, "RAID..");
	}
	newtPushHelpLine
		("       Edit this partition's mountpoint, size and format; then click 'OK'.");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, deviceComp, mountpointComp, sizeComp,
						  formatComp, label0, label1, label2, label3, bOK,
						  bCancel, b_raid, NULL);
	for (b_res = NULL; b_res != bOK && b_res != bCancel;) {
		b_res = newtRunForm(myForm);
		strcpy(device_str, device_here);
		strip_spaces(device_str);
		strcpy(mountpoint_str, mountpoint_here);
		strip_spaces(mountpoint_str);
		strcpy(format_str, format_here);
		strip_spaces(format_str);
		if (b_res == bOK && strstr(device_str, RAID_DEVICE_STUB)
			&& strstr(device_used_to_be, RAID_DEVICE_STUB)
			&& strcmp(device_str, device_used_to_be)) {
			popup_and_OK("You can't change /dev/mdX to /dev/mdY.");
			b_res = NULL;
			continue;
		} else if (b_res == bOK && !strcmp(mountpoint_str, "image")
				   && strcmp(mountpt_used_to_be, "image")) {
			popup_and_OK("You can't change a regular device to an image.");
			b_res = NULL;
			continue;
		}
		if (!strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)
			&& strcmp(mountlist->el[currline].mountpoint, "image")) {
			strcpy(size_str, size_here);
			strip_spaces(size_str);
		} else {
			sprintf(size_str, "%ld",
					calculate_raid_device_size(mountlist, raidlist,
											   mountlist->el[currline].
											   device) / 1024);
			newtLabelSetText(sizeComp, size_str);
		}
		/* do not let user click RAID button if user has changed device_str */
		if (b_res == b_raid) {
			if (strcmp(device_str, mountlist->el[currline].device)) {
				/*
				   can't change mountlist's entry from /dex/mdX to /dev/mdY: it would ugly 
				   when you try to map the changes over to the raidtab list, trust me
				 */
				popup_and_OK
					("You cannot edit the RAID settings until you have OK'd your change to the device node.");
			} else {
				j = find_raid_device_in_raidlist(raidlist,
												 mountlist->el[currline].
												 device);
				if (j < 0) {
					sprintf(tmp,
							"/etc/raidtab does not have an entry for %s; please delete it and add it again",
							mountlist->el[currline].device);
					popup_and_OK(tmp);
				} else {
					log_it("edit_raidlist_entry - calling");
					edit_raidlist_entry(mountlist, raidlist,
										&raidlist->el[j], currline);
				}
			}
		}
	}
	newtFormDestroy(myForm);
	newtPopHelpLine();
	newtPopWindow();
	if (b_res == bCancel) {
		memcpy((void *) raidlist, (void *) &bkp_raidlist,
			   sizeof(struct raidlist_itself));
		return;
	}
	strcpy(mountlist->el[currline].device, device_str);
	strcpy(mountlist->el[currline].mountpoint, mountpoint_str);
	strcpy(mountlist->el[currline].format, format_str);
	if (strcmp(mountlist->el[currline].mountpoint, "image")) {
		if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)) {
			mountlist->el[currline].size =
				calculate_raid_device_size(mountlist, raidlist,
									   mountlist->el[currline].device);
		} else {
			mountlist->el[currline].size = atol(size_str) * 1024;
		}
	}
	newtListboxSetEntry(listbox, (long) keylist[currline],
						mountlist_entry_to_string(mountlist, currline));
	/* if new /dev/md RAID device then do funky stuff */
	if (strstr(mountlist->el[currline].device, RAID_DEVICE_STUB)
		&& !strstr(device_used_to_be, RAID_DEVICE_STUB)) {
		initiate_new_raidlist_entry(raidlist, mountlist, currline,
									device_str);
	}
	/* if moving from RAID to non-RAID then do funky stuff */
	else if (strstr(device_used_to_be, RAID_DEVICE_STUB)
			 && !strstr(device_str, RAID_DEVICE_STUB)) {
		delete_raidlist_entry(mountlist, raidlist, device_str);
	}
	/* if moving a non-RAID to another non-RAID then re-jig any RAID disks, if necessary */
	else if (!strstr(device_used_to_be, RAID_DEVICE_STUB)
			 && !strstr(device_str, RAID_DEVICE_STUB)) {
		rejig_partition_name_in_raidlist_if_necessary(raidlist,
													  device_used_to_be,
													  device_str);
	}
/* else, moving a RAID to another RAID; bad idea, or so I thought */
#ifndef __FreeBSD__				/* It works fine under FBSD. */
	else if (strcmp(device_used_to_be, device_str)) {
		popup_and_OK
			("You are renaming a RAID device as another RAID device. I don't like it but I'll allow it.");
	}
#endif
	redraw_mountlist(mountlist, keylist, listbox);
}


#if __FreeBSD__
/**
 * Add a subdisk to @p raidrec.
 * @param raidlist The raidlist containing information about RAID partitions.
 * @param raidrec The RAID device record to add the subdisk to.
 * @param temp The device name of the RAID disk to add it to.
 * @author Joshua Oreman
 * @ingroup restoreGuiMountlist
 */
void
add_raid_subdisk(struct raidlist_itself *raidlist,
				 struct vinum_plex *raidrec, char *temp)
{
	int i;
	bool found = FALSE;

	for (i = 0; i < raidlist->disks.entries; ++i) {
		if (!strcmp(raidlist->disks.el[i].device, temp)) {
			strcpy(raidrec->sd[raidrec->subdisks].which_device,
				   raidlist->disks.el[i].name);
			found = TRUE;
		}
	}
	if (!found) {
		sprintf(raidlist->disks.el[raidlist->disks.entries].name,
				"drive%i", raidlist->disks.entries);
		sprintf(raidrec->sd[raidrec->subdisks].which_device, "drive%i",
				raidlist->disks.entries);
		strcpy(raidlist->disks.el[raidlist->disks.entries++].device, temp);
	}
	raidrec->subdisks++;
}


/**
 * Determine the /dev entry for @p vinum_name.
 * @param raidlist The raidlist containing information about RAID devices.
 * @param vinum_name The name of the Vinum drive to map to a /dev entry.
 * @return The /dev entry, or NULL if none was found.
 * @note The returned string points to static storage that will be overwritten with each call.
 * @author Joshua Oreman
 * @ingroup restoreUtilityGroup
 */
char *find_dev_entry_for_raid_device_name(struct raidlist_itself *raidlist,
										  char *vinum_name)
{
	int i;
	for (i = 0; i < raidlist->disks.entries; ++i) {
		if (!strcmp(raidlist->disks.el[i].name, vinum_name)) {
			return raidlist->disks.el[i].device;
		}
	}
	return NULL;
}

void
edit_raidlist_plex(struct mountlist_itself *mountlist,
				   struct raidlist_itself *raidlist,
				   struct vinum_plex *raidrec, int currline,
				   int currline2);

#endif


/**
 * Edit the entry for @p raidrec in @p raidlist.
 * @param mountlist The mountlist to get some information from.
 * @param raidlist The raidlist containing information about RAID devices.
 * @param raidrec The RAID device record for this partition.
 * @param currline The line number (starting from 0) in the mountlist of the RAID device.
 * @ingroup restoreGuiMountlist
 */
void
edit_raidlist_entry(struct mountlist_itself *mountlist,
					struct raidlist_itself *raidlist,
					struct OSSWAP (raid_device_record,
								   vinum_volume) * raidrec, int currline)
{

#ifdef __FreeBSD__
	/** structures ********************************************************/
	struct vinum_volume bkp_raidrec;


	/** buffers ***********************************************************/
	char title_of_editraidForm_window[MAX_STR_LEN];

	/** newt **************************************************************/
	newtComponent editraidForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent bEdit;
	newtComponent bAdd;
	newtComponent bDelete;
	newtComponent b_res;
	newtComponent plexesListbox;
	newtComponent plexesHeader;

	void *keylist[10];
	void *curr_choice;

	int currline2 = 0;

	log_it("Started edit_raidlist_entry");
	memcpy((void *) &bkp_raidrec, (void *) raidrec,
		   sizeof(struct vinum_volume));
	sprintf(title_of_editraidForm_window, "Plexes on %s",
			raidrec->volname);
	newtPushHelpLine("   Please select a plex to edit");
	newtOpenWindow(13, 5, 54, 15, title_of_editraidForm_window);
	for (;;) {
		int i;
		char headerstr[MAX_STR_LEN];
		snprintf(headerstr, MAX_STR_LEN, "%-14s %-8s  %11s  %8s",
				 "Plex", "Level", "Stripe Size", "Subdisks");

		bOK = newtCompactButton(2, 13, "  OK  ");
		bCancel = newtCompactButton(12, 13, "Cancel");
		bAdd = newtCompactButton(22, 13, " Add ");
		bEdit = newtCompactButton(32, 13, " Edit ");
		bDelete = newtCompactButton(42, 13, "Delete");

		plexesListbox =
			newtListbox(2, 3, 9, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
		plexesHeader = newtLabel(2, 2, headerstr);
		editraidForm = newtForm(NULL, NULL, 0);

		newtListboxClear(plexesListbox);
		for (i = 0; i < 10; ++i) {
			keylist[i] = (void *) i;
			if (i < raidrec->plexes) {
				char pname[64], entry[MAX_STR_LEN], raidlevel[64],
					chunksize[64];
				switch (raidrec->plex[i].raidlevel) {
				case -1:
					strcpy(raidlevel, "concat");
					break;
				case 0:
					strcpy(raidlevel, "striped");
					break;
				case 5:
					strcpy(raidlevel, "raid5");
					break;
				default:
					sprintf(raidlevel, "raid%i",
							raidrec->plex[i].raidlevel);
					break;
				}

				if (raidrec->plex[i].raidlevel == -1) {
					strcpy(chunksize, "N/A");
				} else {
					sprintf(chunksize, "%dk", raidrec->plex[i].stripesize);
				}
				snprintf(pname, 64, "%s.p%i", raidrec->volname, i);
				snprintf(entry, MAX_STR_LEN, "%-14s %-8s  %11s  %8d",
						 pname, raidlevel, chunksize,
						 raidrec->plex[i].subdisks);
				newtListboxAppendEntry(plexesListbox, entry, keylist[i]);
			}
		}

		newtFormAddComponents(editraidForm, bOK, bCancel, bAdd, bEdit,
							  bDelete, plexesListbox, plexesHeader, NULL);

		b_res = newtRunForm(editraidForm);
		if (b_res == bOK || b_res == bCancel) {
			break;
		}

		curr_choice = newtListboxGetCurrent(plexesListbox);
		for (currline2 = 0; currline2 < raidrec->plexes; ++currline2) {
			if (currline2 > 9)
				break;
			if (keylist[currline2] == curr_choice)
				break;
		}

		if (b_res == bDelete) {
			char msg[MAX_STR_LEN];
			sprintf(msg, "Are you sure you want to delete %s.p%i?",
					raidrec->volname, currline2);
			if (ask_me_yes_or_no(msg)) {
				log_it("Deleting RAID plex");
				memcpy((void *) &raidrec->plex[currline2],
					   (void *) &raidrec->plex[raidrec->plexes - 1],
					   sizeof(struct vinum_plex));
				raidrec->plexes--;
			}
			continue;
		}
		if (b_res == bAdd) {
			raidrec->plex[raidrec->plexes].raidlevel = 0;
			raidrec->plex[raidrec->plexes].stripesize = 279;
			raidrec->plex[raidrec->plexes].subdisks = 0;
			currline2 = raidrec->plexes++;
		}
		edit_raidlist_plex(mountlist, raidlist, &raidrec->plex[currline2],
						   currline, currline2);
		newtFormDestroy(editraidForm);
	}
	if (b_res == bCancel) {
		memcpy((void *) raidrec, (void *) &bkp_raidrec,
			   sizeof(struct vinum_volume));
	}
	newtPopHelpLine();
	newtPopWindow();
	mountlist->el[currline].size =
		calculate_raid_device_size(mountlist, raidlist, raidrec->volname);
#else
	/** structures ********************************************************/
	struct raid_device_record *bkp_raidrec;


	/** buffers ***********************************************************/
	char *title_of_editraidForm_window;
	char *sz_raid_level;
	char *sz_data_disks;
	char *sz_spare_disks;
	char *sz_parity_disks;
	char *sz_failed_disks;

	/** newt **************************************************************/
	newtComponent editraidForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent bAdditional;
	newtComponent bChangeRaid;
	newtComponent bSelectData;
	newtComponent bSelectSpare;
	newtComponent bSelectParity;
	newtComponent bSelectFailed;
	newtComponent b_res;

	assert(mountlist != NULL);
	assert(raidlist != NULL);
	assert(raidrec != NULL);

	malloc_string(title_of_editraidForm_window);
	malloc_string(sz_raid_level);
	malloc_string(sz_data_disks);
	malloc_string(sz_spare_disks);
	malloc_string(sz_parity_disks);
	malloc_string(sz_failed_disks);
	if (!(bkp_raidrec = malloc(sizeof(struct raid_device_record)))) {
		fatal_error("Cannot malloc space for raidrec");
	}

	log_it("Started edit_raidlist_entry");

	memcpy((void *) bkp_raidrec, (void *) raidrec,
		   sizeof(struct raid_device_record));
	sprintf(title_of_editraidForm_window, "%s", raidrec->raid_device);
	log_msg(2, "Opening newt window");
	newtOpenWindow(20, 5, 40, 14, title_of_editraidForm_window);
	for (;;) {
		log_msg(2, "Main loop");
		sprintf(title_of_editraidForm_window, "Edit %s",
				raidrec->raid_device);
		strcpy(sz_raid_level,
			   turn_raid_level_number_to_string(raidrec->raid_level));
		strcpy(sz_data_disks,
			   number_of_disks_as_string(raidrec->data_disks.entries,
										 "data"));
		strcpy(sz_spare_disks,
			   number_of_disks_as_string(raidrec->spare_disks.entries,
										 "spare"));
		strcpy(sz_parity_disks,
			   number_of_disks_as_string(raidrec->parity_disks.entries,
										 "parity"));
		strcpy(sz_failed_disks,
			   number_of_disks_as_string(raidrec->failed_disks.entries,
										 "failed"));
		bSelectData = newtButton(1, 1, sz_data_disks);
		bSelectSpare = newtButton(20, 1, sz_spare_disks);
		bSelectParity = newtButton(1, 5, sz_parity_disks);
		bSelectFailed = newtButton(20, 5, sz_failed_disks);
		bChangeRaid = newtButton(1, 9, sz_raid_level);
		bOK = newtButton(16 + (raidrec->raid_level == -1), 9, "  OK  ");
		bCancel = newtButton(28, 9, "Cancel");
		bAdditional =
			newtCompactButton(1, 13,
							  "Additional settings and information");
		newtPushHelpLine
			("  Edit the RAID device's settings to your heart's content, then hit OK/Cancel.");
		editraidForm = newtForm(NULL, NULL, 0);
		newtFormAddComponents(editraidForm, bSelectData, bSelectParity,
							  bChangeRaid, bSelectSpare, bSelectFailed,
							  bOK, bCancel, bAdditional);
		b_res = newtRunForm(editraidForm);
		if (b_res == bChangeRaid) {
			choose_raid_level(raidrec);
		} else if (b_res == bSelectData) {
			select_raid_disks(mountlist, raidlist, raidrec, "data",
							  &raidrec->data_disks);
		} else if (b_res == bSelectSpare) {
			select_raid_disks(mountlist, raidlist, raidrec, "spare",
							  &raidrec->spare_disks);
		} else if (b_res == bSelectParity) {
			select_raid_disks(mountlist, raidlist, raidrec, "parity",
							  &raidrec->parity_disks);
		} else if (b_res == bSelectFailed) {
			select_raid_disks(mountlist, raidlist, raidrec, "failed",
							  &raidrec->failed_disks);
		} else if (b_res == bAdditional) {
			edit_raidrec_additional_vars(raidrec);
		}
		newtFormDestroy(editraidForm);
		if (b_res == bOK || b_res == bCancel) {
			break;
		}
	}
	if (b_res == bCancel) {
		memcpy((void *) raidrec, (void *) bkp_raidrec,
			   sizeof(struct raid_device_record));
	}
	newtPopHelpLine();
	newtPopWindow();
	mountlist->el[currline].size =
		calculate_raid_device_size(mountlist, raidlist,
								   raidrec->raid_device);
	paranoid_free(title_of_editraidForm_window);
	paranoid_free(sz_raid_level);
	paranoid_free(sz_data_disks);
	paranoid_free(sz_spare_disks);
	paranoid_free(sz_parity_disks);
	paranoid_free(sz_failed_disks);
	paranoid_free(bkp_raidrec);
#endif
}

#ifdef __FreeBSD__

/**
 * Edit the plex @p raidrec in @p raidlist.
 * @param mountlist The mountlist to get some of the information from.
 * @param raidlist The raidlist containing information about RAID devices.
 * @param raidrec The plex to edit.
 * @param currline The line number (starting from 0) of the RAID device in @p mountlist.
 * @param currline2 The line number (starting from 0) of the plex within the RAID device.
 * @author Joshua Oreman
 * @ingroup restoreGuiMountlist
 */
void
edit_raidlist_plex(struct mountlist_itself *mountlist,
				   struct raidlist_itself *raidlist,
				   struct vinum_plex *raidrec, int currline, int currline2)
{

	/** structures ********************************************************/
	struct vinum_plex bkp_raidrec;


	/** buffers ***********************************************************/
	char title_of_editraidForm_window[MAX_STR_LEN];

	/** newt **************************************************************/
	newtComponent editraidForm;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent bEdit;
	newtComponent bAdd;
	newtComponent bDelete;
	newtComponent b_res;
	newtComponent unallocListbox, allocListbox;
	newtComponent bLevel, sLevel;
	newtComponent bStripeSize, sStripeSize;
	newtComponent bAlloc, bUnalloc;

	void *keylist[ARBITRARY_MAXIMUM];
	void *curr_choice_a, *curr_choice_u;
	int currline_a, currline_u;

	struct mountlist_itself *unallocparts;

	unallocparts = malloc(sizeof(struct mountlist_itself));

	log_it("Started edit_raidlist_entry");
	memcpy((void *) &bkp_raidrec, (void *) raidrec,
		   sizeof(struct vinum_plex));
	sprintf(title_of_editraidForm_window, "%s.p%i",
			raidlist->el[currline].volname, currline2);
	newtPushHelpLine
		("   Please select a subdisk to edit, or edit this plex's parameters");
	newtOpenWindow(13, 3, 54, 18, title_of_editraidForm_window);
	for (;;) {
		int i;
		char headerstr[MAX_STR_LEN];
		char tmp[64];
		snprintf(headerstr, MAX_STR_LEN, "%-24s %s", "Subdisk", "Device");


		switch (raidrec->raidlevel) {
		case -1:
			strcpy(tmp, "concat");
			break;
		case 0:
			strcpy(tmp, "striped");
			break;
		case 5:
			strcpy(tmp, "raid5");
			break;
		default:
			sprintf(tmp, "unknown (%i)", raidrec->raidlevel);
			break;
		}
		bLevel = newtCompactButton(2, 2, " RAID level ");
		sLevel = newtLabel(19, 2, tmp);

		if (raidrec->raidlevel >= 0) {
			sprintf(tmp, "%ik", raidrec->stripesize);
			bStripeSize = newtCompactButton(2, 4, " Stripe size ");
		} else {
			strcpy(tmp, "N/A");
			bStripeSize = newtLabel(2, 4, "Stripe size:");
		}
		sStripeSize = newtLabel(19, 4, tmp);

		bOK = newtCompactButton(2, 16, "  OK  ");
		bCancel = newtCompactButton(12, 16, "Cancel");
		bAdd = newtCompactButton(22, 16, " Add ");
		bEdit = newtCompactButton(32, 16, " Edit ");
		bDelete = newtCompactButton(42, 16, "Delete");


		//  plexesListbox = newtListbox (2, 7, 9, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
		//  plexesHeader  = newtLabel (2, 6, headerstr);
		unallocListbox =
			newtListbox(2, 7, 7, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
		allocListbox =
			newtListbox(33, 7, 7, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
		bAlloc = newtButton(23, 7, " -> ");
		bUnalloc = newtButton(23, 11, " <- ");

		editraidForm = newtForm(NULL, NULL, 0);

		newtListboxClear(allocListbox);
		newtListboxClear(unallocListbox);
		bzero(unallocparts, sizeof(struct mountlist_itself));
		make_list_of_unallocated_raid_partitions(unallocparts, mountlist,
												 raidlist);
		for (i = 0; i < ARBITRARY_MAXIMUM; ++i) {
			char entry[MAX_STR_LEN];
			keylist[i] = (void *) i;
			if (i < raidrec->subdisks) {
				snprintf(entry, MAX_STR_LEN, "%-17s",
						 find_dev_entry_for_raid_device_name(raidlist,
															 raidrec->
															 sd[i].
															 which_device));
				newtListboxAppendEntry(allocListbox, entry, keylist[i]);
			}
			if (i < unallocparts->entries) {
				snprintf(entry, MAX_STR_LEN, "%-17s",
						 unallocparts->el[i].device);
				newtListboxAppendEntry(unallocListbox, entry, keylist[i]);
			}
		}

#define COMP(x)  newtFormAddComponent (editraidForm, x)
#define UCOMP(x) if (unallocparts->entries > 0) COMP(x)
#define ACOMP(x) if (raidrec->subdisks > 0) COMP(x)
		editraidForm = newtForm(NULL, NULL, 0);
		UCOMP(unallocListbox);
		UCOMP(bAlloc);
		ACOMP(allocListbox);
		ACOMP(bUnalloc);
		COMP(bOK);
		COMP(bCancel);
		COMP(bLevel);
		COMP(sLevel);
		if (raidrec->raidlevel != -1) {
			COMP(bStripeSize);
			COMP(sStripeSize);
		}
#undef COMP
#undef UCOMP
#undef ACOMP

		newtRefresh();
		b_res = newtRunForm(editraidForm);
		if (b_res == bOK || b_res == bCancel) {
			break;
		}

		curr_choice_a = (raidrec->subdisks > 0) ?
			newtListboxGetCurrent(allocListbox) : (void *) 1234;
		curr_choice_u = (unallocparts->entries > 0) ?
			newtListboxGetCurrent(unallocListbox) : (void *) 1234;
		for (currline_a = 0; currline_a < raidrec->subdisks; ++currline_a) {
			if (currline_a > ARBITRARY_MAXIMUM)
				break;
			if (keylist[currline_a] == curr_choice_a)
				break;
		}
		for (currline_u = 0; currline_u < unallocparts->entries;
			 ++currline_u) {
			if (currline_u > ARBITRARY_MAXIMUM)
				break;
			if (keylist[currline_u] == curr_choice_u)
				break;
		}
		if (b_res == bLevel) {
			choose_raid_level(raidrec);
		} else if (b_res == bStripeSize) {
			char tmp[64];
			sprintf(tmp, "%i", raidrec->stripesize);
			if (popup_and_get_string
				("Stripe size",
				 "Please enter the stripe size in kilobytes.", tmp, 20)) {
				raidrec->stripesize = atoi(tmp);
			}
		} else if ((b_res == bAlloc) || (b_res == unallocListbox)) {
			if (currline_u <= unallocparts->entries)
				add_raid_subdisk(raidlist, raidrec,
								 unallocparts->el[currline_u].device);
		} else if ((b_res == bUnalloc) || (b_res == allocListbox)) {
			if (currline_a <= raidrec->subdisks) {
				memcpy((void *) &raidrec->sd[currline_a],
					   (void *) &raidrec->sd[raidrec->subdisks - 1],
					   sizeof(struct vinum_subdisk));
				raidrec->subdisks--;
			}
		}
#if 0
	} else {
		edit_raid_subdisk(raidlist, raidrec, &raidrec->sd[currline3],
						  currline3);
	}
#endif
	newtFormDestroy(editraidForm);
	newtRefresh();
}

if (b_res == bCancel) {
	memcpy((void *) raidrec, (void *) &bkp_raidrec,
		   sizeof(struct vinum_plex));
}
newtPopWindow();
newtPopHelpLine();
}
#else
/**
 * Edit additional RAID variable number @p lino.
 * @param raidrec The RAID device record to edit the variable in.
 * @param lino The line number (starting from 0) of the variable to edit.
 * @ingroup restoreGuiVarslist
 */
void edit_varslist_entry(struct raid_device_record *raidrec, int lino)
{

	/** buffers ***********************************************************/
	char header[MAX_STR_LEN];
	char comment[MAX_STR_LEN];
	char sz_out[MAX_STR_LEN];

	assert(raidrec != 0);
	assert(lino >= 0);

	strcpy(sz_out, raidrec->additional_vars.el[lino].value);
	sprintf(header, "Edit %s", raidrec->additional_vars.el[lino].label);
	sprintf(comment, "Please set %s's value (currently '%s')",
			raidrec->additional_vars.el[lino].label, sz_out);
	if (popup_and_get_string(header, comment, sz_out, MAX_STR_LEN)) {
		strip_spaces(sz_out);
		strcpy(raidrec->additional_vars.el[lino].value, sz_out);
	}
}


/* I'm not racist against white people. I just don't like people who think Liberia is near Spain.       - Hugo, 09/01/2001 */

#endif

/**
 * Edit the mountlist using Newt.
 * @param mountlist The mountlist to edit.
 * @param raidlist The raidlist that goes with @p mountlist.
 * @return 0 if the user pressed OK, 1 if they pressed Cancel.
 */
int
edit_mountlist_in_newt(char *mountlist_fname,
					   struct mountlist_itself *mountlist,
					   struct raidlist_itself *raidlist)
{

	/** newt **************************************************************/
	newtComponent myForm;
	newtComponent bAdd;
	newtComponent bEdit;
	newtComponent bDelete;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent b_res = NULL;
	newtComponent partitionsListbox;
	newtComponent headerMsg;
	newtComponent flawsLabelA;
	newtComponent flawsLabelB;
	newtComponent flawsLabelC;
	newtComponent bReload;

	/** ???? *************************************************************/
	void *curr_choice;
	void *keylist[ARBITRARY_MAXIMUM];

	/** int **************************************************************/
	int i = 0;
	int currline = 0;
	int finished = FALSE;

	/** buffers **********************************************************/
	char tmp[MAX_STR_LEN];
	char flaws_str_A[MAX_STR_LEN];
	char flaws_str_B[MAX_STR_LEN];
	char flaws_str_C[MAX_STR_LEN];

	assert(mountlist != NULL);
	assert(raidlist != NULL);

	strcpy(flaws_str_A, "xxxxxxxxx");
	strcpy(flaws_str_B, "xxxxxxxxx");
	strcpy(flaws_str_C, "xxxxxxxxx");
	if (mountlist->entries > ARBITRARY_MAXIMUM) {
		log_to_screen("Arbitrary limits suck, man!");
		finish(1);
	}
	newtPushHelpLine
		("   Please edit the mountlist to your satisfaction, then click OK or Cancel.");
	i = 4;
	bAdd = newtCompactButton(i, 17, " Add ");
	bEdit = newtCompactButton(i += 11, 17, " Edit ");
	bDelete = newtCompactButton(i += 12, 17, "Delete");
	bReload = newtCompactButton(i += 12, 17, "Reload");
	bCancel = newtCompactButton(i += 12, 17, "Cancel");
	bOK = newtCompactButton(i += 12, 17, "  OK  ");
	sprintf(tmp, "%-24s %-24s %-8s  %s", "Device", "Mountpoint", "Format",
			"Size (MB)");
	headerMsg = newtLabel(2, 1, tmp);
	flawsLabelA = newtLabel(2, 13, flaws_str_A);
	flawsLabelB = newtLabel(2, 14, flaws_str_B);
	flawsLabelC = newtLabel(2, 15, flaws_str_C);
	partitionsListbox =
		newtListbox(2, 2, 10, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
	redraw_mountlist(mountlist, keylist, partitionsListbox);
	newtOpenWindow(1, 3, 77, 18, "Editing mountlist");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, headerMsg, partitionsListbox,
						  flawsLabelA, flawsLabelB, flawsLabelC, bAdd,
						  bEdit, bDelete, bReload, bCancel, bOK, NULL);
	while (!finished) {
		evaluate_mountlist(mountlist, flaws_str_A, flaws_str_B,
						   flaws_str_C);
		newtLabelSetText(flawsLabelA, flaws_str_A);
		newtLabelSetText(flawsLabelB, flaws_str_B);
		newtLabelSetText(flawsLabelC, flaws_str_C);
		b_res = newtRunForm(myForm);
		if (b_res == bOK) {
			if (!evaluate_mountlist
				(mountlist, flaws_str_A, flaws_str_B, flaws_str_C)) {
				finished =
					ask_me_yes_or_no
					("Your mountlist might not work. Continue anyway?");
			} else {
				finished =
					ask_me_yes_or_no
					("Are you sure you want to save your mountlist and continue? (No changes will be made to your partition table at this time.)");
			}
		} else if (b_res == bCancel) {
			finished = TRUE;
		} else if (b_res == bReload) {
			if (ask_me_yes_or_no("Reload original mountlist?")) {
/*
This would be really dumb. RAIDTAB_FNAME is #define'd.   --- Hugo, 2003/04/24
	      if (!RAIDTAB_FNAME[0])
		{
                  strcpy(RAIDTAB_FNAME, "/etc/raidtab");
 		  log_it("Warning - raidtab_fname is blank. Assuming %s", g_raidtab_fname);
		}
*/
				load_mountlist(mountlist, mountlist_fname);
				load_raidtab_into_raidlist(raidlist, RAIDTAB_FNAME);
				redraw_mountlist(mountlist, keylist, partitionsListbox);
			}
		} else {
			curr_choice = newtListboxGetCurrent(partitionsListbox);
			for (i = 0;
				 i < mountlist->entries && keylist[i] != curr_choice; i++);
			if (i == mountlist->entries && mountlist->entries > 0) {
				log_to_screen("I don't know what that button does!");
			} else {
				currline = i;
				if (b_res == bAdd) {
					add_mountlist_entry(mountlist, raidlist,
										partitionsListbox, currline,
										keylist);
				} else if (b_res == bDelete) {
					delete_mountlist_entry(mountlist, raidlist,
										   partitionsListbox, currline,
										   keylist);
				} else {
					if (mountlist->entries > 0) {
						edit_mountlist_entry(mountlist, raidlist,
											 partitionsListbox, currline,
											 keylist);
					} else {
						popup_and_OK
							("Please add an entry. Then press ENTER to edit it.");
					}
				}
			}
		}
	}
	newtFormDestroy(myForm);
	newtPopWindow();
	newtPopHelpLine();
	if (b_res == bOK) {
		log_it("You pushed 'OK'. I shall now continue.");
		return (0);
	} else {
		/* popup_and_OK("You pushed 'cancel'. I shall now abort."); */
		return (1);
	}
}



/**
 * Edit the mountlist.
 * @param mountlist The mountlist to edit.
 * @param raidlist The raidlist that goes with @p mountlist.
 * @return 0 if the user pressed OK, 1 if they pressed Cancel.
 */
int
edit_mountlist(char *mountlist_fname, struct mountlist_itself *mountlist,
			   struct raidlist_itself *raidlist)
{
	int res = 0;
//  char tmp[MAX_STR_LEN];

	iamhere("entering eml");

	if (g_text_mode) {
		fatal_error("Don't call edit_mountlist() in text mode");
	} else {
		log_it
			("I'm in GUI mode, so I shall edit mountlist using edit_mountlist()");
		res = edit_mountlist_in_newt(mountlist_fname, mountlist, raidlist);
	}
	iamhere("leaving eml");
	return (res);
}




#ifndef __FreeBSD__
/**
 * Edit the additional RAID variables in @p raidrec.
 * @param raidrec The RAID device record to edit the RAID variables in.
 * @ingroup restoreGuiVarslist
 */
void edit_raidrec_additional_vars(struct raid_device_record *raidrec)
{

	/** structure *********************************************************/
	struct raid_device_record bkp_raidrec;

	/** newt **************************************************************/
	newtComponent myForm;
	newtComponent bAdd;
	newtComponent bEdit;
	newtComponent bDelete;
	newtComponent bOK;
	newtComponent bCancel;
	newtComponent b_res;
	newtComponent varsListbox;
	newtComponent headerMsg;

	/** ?? ***************************************************************/
	void *keylist[ARBITRARY_MAXIMUM], *curr_choice;

	/** buffers **********************************************************/
	char title_of_window[MAX_STR_LEN];

	/** int **************************************************************/
	int i = 0;
	int currline = 0;


	assert(raidrec != NULL);

	memcpy((void *) &bkp_raidrec, (void *) raidrec,
		   sizeof(struct raid_device_record));
	sprintf(title_of_window, "Additional variables");
	newtPushHelpLine
		("  Edit the additional fields to your heart's content, then click OK or Cancel.");
	headerMsg = newtLabel(1, 1, "Label                            Value");
	varsListbox =
		newtListbox(1, 2, 6, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
	i = 1;
	bAdd = newtCompactButton(i, 9, " Add ");
	bEdit = newtCompactButton(i += 8, 9, " Edit ");
	bDelete = newtCompactButton(i += 9, 9, "Delete");
	bOK = newtCompactButton(i += 9, 9, "  OK  ");
	bCancel = newtCompactButton(i += 9, 9, "Cancel");
	newtOpenWindow(17, 7, 46, 10, title_of_window);
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, headerMsg, varsListbox, bAdd, bEdit,
						  bDelete, bOK, bCancel, NULL);
	insert_essential_additionalvars(raidrec);
	redraw_varslist(&raidrec->additional_vars, keylist, varsListbox);
	for (b_res = NULL; b_res != bOK && b_res != bCancel;) {
		b_res = newtRunForm(myForm);
		curr_choice = newtListboxGetCurrent(varsListbox);
		for (currline = 0;
			 currline < raidrec->additional_vars.entries
			 && keylist[currline] != curr_choice; currline++);
		if (currline == raidrec->additional_vars.entries
			&& raidrec->additional_vars.entries > 0) {
			log_it("Warning - I don't know what this button does");
		}
		if (b_res == bOK) {		/* do nothing */
		} else if (b_res == bCancel) {	/* do nothing */
		} else if (b_res == bAdd) {
			add_varslist_entry(raidrec);
		} else if (b_res == bDelete) {
			delete_varslist_entry(raidrec, currline);
		} else {
			edit_varslist_entry(raidrec, currline);
		}
		redraw_varslist(&raidrec->additional_vars, keylist, varsListbox);
	}
	remove_essential_additionalvars(raidrec);
	newtFormDestroy(myForm);
	newtPopWindow();
	newtPopHelpLine();
	if (b_res == bCancel) {
		memcpy((void *) raidrec, (void *) &bkp_raidrec,
			   sizeof(struct raid_device_record));
	}
	return;
}
#endif


/**
 * Find the next free location to place a disk in @p disklist.
 * @param disklist The disklist to operate on.
 * @return The next free location (starting from 0).
 * @ingroup restoreGuiDisklist
 */
int find_next_free_index_in_disklist(struct list_of_disks *disklist)
{

	/** int ***************************************************************/
	int index = -1;
	int pos = 0;

  /** bool **************************************************************/
	bool done;

	assert(disklist != NULL);

	for (done = FALSE; !done;) {
		for (pos = 0;
			 pos < disklist->entries && disklist->el[pos].index <= index;
			 pos++);
		if (pos >= disklist->entries) {
			done = TRUE;
		} else {
			index = disklist->el[pos].index;
		}
	}
	return (index + 1);
}



/**
 * Locate @p device in @p raidlist.
 * @param raidlist The raidlist ot search in.
 * @param device The RAID device to search for.
 * @return The index of the device, or -1 if it could not be found.
 * @ingroup restoreGuiMountlist
 */
int
find_raid_device_in_raidlist(struct raidlist_itself *raidlist,
							 char *device)
{

	/** int ***************************************************************/
	int i = 0;
#ifdef __FreeBSD__
	char vdev[64];
#else
// Linux
#endif

	assert(raidlist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(device);

#ifdef __FreeBSD__
	for (i = 0; i < raidlist->entries; i++) {
		sprintf(vdev, "/dev/vinum/%s", raidlist->el[i].volname);
		if (!strcmp(device, vdev))
			break;
	}
#else

	for (i = 0;
		 strcmp(raidlist->el[i].raid_device, device)
		 && i < raidlist->entries; i++);
#endif
	if (i == raidlist->entries) {
		return (-1);
	} else {
		return (i);
	}
}


/**
 * Get information about the location of ISO images from the user.
 * @param isodir_device Where to put the device (e.g. /dev/hda4) the user enters.
 * @param isodir_format Where to put the format (e.g. ext2) the user enters.
 * @param isodir_path Where to put the path (e.g. /root/images/mondo) the user enters.
 * @param nuke_me_please Whether we're planning on nuking or not.
 * @return TRUE if OK was pressed, FALSE otherwise.
 */
bool
get_isodir_info(char *isodir_device, char *isodir_format,
				char *isodir_path, bool nuke_me_please)
{

	/** initialize ********************************************************/

	assert(isodir_device != NULL);
	assert(isodir_format != NULL);
	assert(isodir_path != NULL);

	log_it("%d - AAA - isodir_path = %s", isodir_path);
	isodir_format[0] = '\0';
	if (isodir_device[0] == '\0') {
		strcpy(isodir_device, "/dev/");
	}
	if (isodir_path[0] == '\0') {
		strcpy(isodir_path, "/");
	}
	if (does_file_exist("/tmp/NFS-SERVER-PATH")) {
		strcpy(isodir_device, last_line_of_file("/tmp/NFS-SERVER-MOUNT"));
		strcpy(isodir_format, "nfs");
		strcpy(isodir_path, last_line_of_file("/tmp/NFS-SERVER-PATH"));
	}
	if (nuke_me_please) {
		return (TRUE);
	}

	if (popup_and_get_string
		("ISO Mode - device", "On what device do the ISO files live?",
		 isodir_device, MAX_STR_LEN / 4)) {
		if (popup_and_get_string
			("ISO Mode - format",
			 "What is the disk format of the device? (Hit ENTER if you don't know.)",
			 isodir_format, 16)) {
			if (popup_and_get_string
				("ISO Mode - path",
				 "At what path on this device can the ISO files be found?",
				 isodir_path, MAX_STR_LEN / 4)) {
				strip_spaces(isodir_device);
				strip_spaces(isodir_format);
				strip_spaces(isodir_path);
				log_it("%d - BBB - isodir_path = %s", isodir_path);
				return (TRUE);
			}
		}
	}
	return (FALSE);
}


/**
 * Create a new raidtab entry for @p device in @p raidlist.
 * @param raidlist The raidlist to add the device to.
 * @param mountlist The mountlist containing information about the user's partitions.
 * @param currline The selected line in the mountlist.
 * @param device The RAID device (e.g. /dev/md0) to use.
 * @ingroup restoreGuiMountlist
 */
void
initiate_new_raidlist_entry(struct raidlist_itself *raidlist,
							struct mountlist_itself *mountlist,
							int currline, char *device)
{

	/** structure *********************************************************/
	struct OSSWAP (raid_device_record, vinum_volume) * raidrec;

	/** int ***************************************************************/
	int pos_in_raidlist = 0;

	assert(raidlist != NULL);
	assert(mountlist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(device);

	pos_in_raidlist =
		find_raid_device_in_raidlist(raidlist,
									 mountlist->el[currline].device);
	if (pos_in_raidlist >= 0) {
		fatal_error("Sorry, that RAID device already exists. Weird.");
	}
	pos_in_raidlist = raidlist->entries++;
	raidrec = &raidlist->el[pos_in_raidlist];
	initialize_raidrec(raidrec);
	strcpy(raidrec->OSSWAP(raid_device, volname),
		   OSSWAP(device, basename(device)));
#ifndef __FreeBSD__
	choose_raid_level(raidrec);
	select_raid_disks(mountlist, raidlist, raidrec, "data",
					  &raidrec->data_disks);
#endif
	edit_raidlist_entry(mountlist, raidlist, raidrec, currline);
}


#ifndef __FreeBSD__
/**
 * Insert the RAID variables not stored in the "additional RAID variables" list there too.
 * @param raidrec The RAID device record to operate on.
 * @ingroup restoreGuiVarslist
 */
void insert_essential_additionalvars(struct raid_device_record *raidrec)
{

	/** int **************************************************************/
	int items = 0;

	assert(raidrec != NULL);

	items = raidrec->additional_vars.entries;
	write_variableINT_to_raid_var_line(raidrec, items++,
									   "persistent-superblock",
									   raidrec->persistent_superblock);
	write_variableINT_to_raid_var_line(raidrec, items++, "chunk-size",
									   raidrec->chunk_size);
	raidrec->additional_vars.entries = items;
}

#endif

/**
 * Dummy function that proves that we can get to the point where Mondo is run.
 */
void nuke_mode_dummy()
{

	/** newt *************************************************************/
	newtComponent myForm;
	newtComponent b1;
	newtComponent b2;
	newtComponent b3;
	newtComponent b_res;


	newtPushHelpLine
		("This is where I nuke your hard drives. Mhahahahaha. No-one can stop Mojo Jojo!");
	newtOpenWindow(24, 3, 32, 13, "Nuking");
	b1 = newtButton(7, 1, "Slowly");
	b2 = newtButton(7, 5, "Medium");
	b3 = newtButton(7, 9, "Quickly");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, b1, b2, b3, NULL);
	b_res = newtRunForm(myForm);
	newtFormDestroy(myForm);
	newtPopWindow();
	newtPopHelpLine();
}



/**
 * Redraw the disklist.
 * @param disklist The disklist to read from.
 * @param keylist The list of keys for @p listbox.
 * @param listbox The Newt listbox component to redraw.
 * @ingroup restoreGuiDisklist
 */
void
redraw_disklist(struct list_of_disks *disklist,
				void *keylist[ARBITRARY_MAXIMUM], newtComponent listbox)
{

	/** long **************************************************************/
	long i = 0;

	assert(disklist != NULL);
	assert(keylist != NULL);
	assert(listbox != NULL);

	newtListboxClear(listbox);

	for (i = 0; i < ARBITRARY_MAXIMUM; i++) {
		keylist[i] = (void *) i;
	}
	for (i = 0; i < disklist->entries; i++) {
		newtListboxAppendEntry(listbox,
							   disklist_entry_to_string(disklist, i),
							   keylist[i]);
	}
}


/**
 * Redraw the mountlist.
 * @param mountlist The mountlist to read from.
 * @param keylist The list of keys for @p listbox.
 * @param listbox The Newt listbox component to redraw.
 * @ingroup restoreGuiMountlist
 */
void
redraw_mountlist(struct mountlist_itself *mountlist,
				 void *keylist[ARBITRARY_MAXIMUM], newtComponent listbox)
{

	/** long **************************************************************/
	long i = 0;

	assert(mountlist != NULL);
	assert(keylist != NULL);
	assert(listbox != NULL);

	newtListboxClear(listbox);
//  sort_mountlist_by_device (mountlist);
	for (i = 0; i < ARBITRARY_MAXIMUM; i++) {
		keylist[i] = (void *) i;
	}
	for (i = 0; i < mountlist->entries; i++) {
		newtListboxAppendEntry(listbox,
							   mountlist_entry_to_string(mountlist, i),
							   keylist[i]);
	}
}




/**
 * Redraw the list of unallocated RAID partitions.
 * @param unallocated_raid_partitions The mountlist containing unallocated RAID partitions.
 * @param keylist The list of keys for @p listbox.
 * @param listbox The Newt listbox component to redraw.
 * @ingroup restoreGuiDisklist
 */
void redraw_unallocpartnslist(struct mountlist_itself
							  *unallocated_raid_partitions,
							  void *keylist[ARBITRARY_MAXIMUM],
							  newtComponent listbox)
{

	/** long *************************************************************/
	long i = 0;

	/** buffers **********************************************************/
	char tmp[MAX_STR_LEN];

	assert(unallocated_raid_partitions != NULL);
	assert(keylist != NULL);
	assert(listbox != NULL);

	newtListboxClear(listbox);
	for (i = 0; i < ARBITRARY_MAXIMUM; i++) {
		keylist[i] = (void *) i;
	}
	for (i = 0; i < unallocated_raid_partitions->entries; i++) {
		sprintf(tmp, "%-22s %8lld",
				unallocated_raid_partitions->el[i].device,
				unallocated_raid_partitions->el[i].size / 1024);
		newtListboxAppendEntry(listbox, tmp, keylist[i]);
	}
}

#ifndef __FreeBSD__
/**
 * Redraw the list of additional RAID variables.
 * @param additional_vars The list of additional RAID varibals.
 * @param keylist The list of keys for @p listbox.
 * @param listbox The Newt listbox component to redraw.
 * @ingroup restoreGuiVarslist
 */
void
redraw_varslist(struct additional_raid_variables *additional_vars,
				void *keylist[], newtComponent listbox)
{
	/** long ************************************************************/
	long i = 0;

	/** buffers *********************************************************/
	char tmp[MAX_STR_LEN];

	assert(additional_vars != NULL);
	assert(keylist != NULL);
	assert(listbox != NULL);

	newtListboxClear(listbox);

	for (i = 0; i < ARBITRARY_MAXIMUM; i++) {
		keylist[i] = (void *) i;
	}
	for (i = 0; i < additional_vars->entries; i++) {
		sprintf(tmp, "%-32s %-8s", additional_vars->el[i].label,
				additional_vars->el[i].value);
		newtListboxAppendEntry(listbox, tmp, keylist[i]);
	}
}


/**
 * Remove variable @p label from the RAID variables list in @p raidrec.
 * @param raidrec The RAID device record to remove the variable from.
 * @param label The variable name to remove.
 * @return The value of the variable removed.
 * @ingroup restoreUtilityGroup
 */
int
read_variableINT_and_remove_from_raidvars(struct
										  OSSWAP (raid_device_record,
												  vinum_volume) * raidrec,
										  char *label)
{
	/** int ***************************************************************/
	int i = 0;
	int res = 0;


	assert(raidrec != NULL);
	assert(label != NULL);

	for (i = 0;
		 i < raidrec->additional_vars.entries
		 && strcmp(raidrec->additional_vars.el[i].label, label); i++);
	if (i == raidrec->additional_vars.entries) {
		res = -1;
	} else {
		res = atoi(raidrec->additional_vars.el[i].value);
		for (i++; i < raidrec->additional_vars.entries; i++) {
			memcpy((void *) &raidrec->additional_vars.el[i - 1],
				   (void *) &raidrec->additional_vars.el[i],
				   sizeof(struct raid_var_line));
		}
		raidrec->additional_vars.entries--;
	}
	return (res);
}
#endif

/**
 * Change all RAID devices to use @p new_dev instead of @p old_dev.
 * @param raidlist The raidlist to make the changes in.
 * @param old_dev The old name of the device (what it used to be).
 * @param new_dev The new name of the device (what it is now).
 * @ingroup restoreGuiMountlist
 */
void rejig_partition_name_in_raidlist_if_necessary(struct raidlist_itself
												   *raidlist,
												   char *old_dev,
												   char *new_dev)
{
	/** buffers ********************************************************/
	char tmp[MAX_STR_LEN];

	/** int ************************************************************/
	int pos = 0;
	int j = 0;

	assert(raidlist != NULL);
	assert_string_is_neither_NULL_nor_zerolength(old_dev);
	assert_string_is_neither_NULL_nor_zerolength(new_dev);

	pos = which_raid_device_is_using_this_partition(raidlist, old_dev);
	if (pos < 0) {
		sprintf(tmp, "No need to rejig %s in raidlist: it's not listed.",
				old_dev);
		log_it(tmp);
	} else {
		if ((j =
			 where_in_drivelist_is_drive(&raidlist->
										 OSSWAP(el[pos].data_disks, disks),
										 old_dev)) >= 0) {
			strcpy(raidlist->OSSWAP(el[pos].data_disks, disks).el[j].
				   device, new_dev);
		} else
			if ((j =
				 where_in_drivelist_is_drive(&raidlist->
											 OSSWAP(el[pos].spare_disks,
													spares),
											 old_dev)) >= 0) {
			strcpy(raidlist->OSSWAP(el[pos].spare_disks, spares).el[j].
				   device, new_dev);
		}
#ifndef __FreeBSD__
		else if ((j =
				  where_in_drivelist_is_drive(&raidlist->el[pos].
											  parity_disks,
											  old_dev)) >= 0) {
			strcpy(raidlist->el[pos].parity_disks.el[j].device, new_dev);
		} else
			if ((j =
				 where_in_drivelist_is_drive(&raidlist->el[pos].
											 failed_disks,
											 old_dev)) >= 0) {
			strcpy(raidlist->el[pos].failed_disks.el[j].device, new_dev);
		}
#endif
		else {
			sprintf(tmp,
					"%s is supposed to be listed in this raid dev but it's not...",
					old_dev);
			log_it(tmp);
		}
	}
}


#ifndef __FreeBSD__
/**
 * Remove the essential RAID variables from the "additional variables" section.
 * If they have been changed, set them in their normal locations too.
 * @param raidrec The RAID device record to operate on.
 * @ingroup restoreUtilityVarslist
 */
void remove_essential_additionalvars(struct raid_device_record *raidrec)
{

	/** int **************************************************************/
	int res = 0;

	assert(raidrec != NULL);

	res =
		read_variableINT_and_remove_from_raidvars(raidrec,
												  "persistent-superblock");
	if (res > 0) {
		raidrec->persistent_superblock = res;
	}
	res = read_variableINT_and_remove_from_raidvars(raidrec, "chunk-size");
	if (res > 0) {
		raidrec->chunk_size = res;
	}
	res = read_variableINT_and_remove_from_raidvars(raidrec, "block-size");
}

/**
 * Select the RAID disks to use in @p raidrec.
 * @param mountlist_dontedit The mountlist (will not be edited).
 * @param raidlist The raidlist to modify.
 * @param raidrec The RAID device record in @p raidlist to work on.
 * @param description_of_list The type of disks we're selecting (e.g. "data").
 * @param disklist The disklist to put the user-selected disks in.
 * @ingroup restoreGuiMountlist
 */
void
select_raid_disks(struct mountlist_itself *mountlist_dontedit,
				  struct raidlist_itself *raidlist,
				  struct raid_device_record *raidrec,
				  char *description_of_list,
				  struct list_of_disks *disklist)
{
	void *curr_choice;

	/** ??? ***************************************************************/

	/** structures ********************************************************/
	struct raidlist_itself *bkp_raidlist;
	struct raid_device_record *bkp_raidrec;
	struct list_of_disks *bkp_disklist;
	struct mountlist_itself *unallocated_raid_partitions;

	/** newt **************************************************************/
	newtComponent myForm = NULL;
	newtComponent bAdd = NULL;
	newtComponent bDelete = NULL;
	newtComponent bOK = NULL;
	newtComponent bCancel = NULL;
	newtComponent b_res = NULL;
	newtComponent partitionsListbox = NULL;
	newtComponent headerMsg = NULL;

	/** buffers **********************************************************/
	void *keylist[ARBITRARY_MAXIMUM];
	char *tmp;
	char *help_text;
	char *title_of_window;
	char *sz_res;
	char *header_text;

  /** int **************************************************************/
	int i = 0;
	int currline = 0;

	assert(mountlist_dontedit != NULL);
	assert(raidlist != NULL);
	assert(raidrec != NULL);
	assert(description_of_list != NULL);
	assert(disklist != NULL);

	iamhere("malloc'ing");
	malloc_string(tmp);
	malloc_string(help_text);
	malloc_string(title_of_window);
	malloc_string(sz_res);
	malloc_string(header_text);
	if (!(bkp_raidrec = malloc(sizeof(struct raid_device_record)))) {
		fatal_error("Cannot malloc space for raidrec");
	}
	if (!(bkp_disklist = malloc(sizeof(struct list_of_disks)))) {
		fatal_error("Cannot malloc space for disklist");
	}
	if (!(bkp_raidlist = malloc(sizeof(struct raidlist_itself)))) {
		fatal_error("Cannot malloc space for raidlist");
	}
	if (!
		(unallocated_raid_partitions =
		 malloc(sizeof(struct mountlist_itself)))) {
		fatal_error("Cannot malloc space for unallocated_raid_partitions");
	}

	memcpy((void *) bkp_raidlist, (void *) raidlist,
		   sizeof(struct raidlist_itself));
	memcpy((void *) bkp_raidrec, (void *) raidrec,
		   sizeof(struct raid_device_record));
	memcpy((void *) bkp_disklist, (void *) disklist,
		   sizeof(struct list_of_disks));

	iamhere("Post-malloc");
	strcpy(help_text,
		   "   Edit this RAID device's list of partitions. Choose OK or Cancel when done.");
	sprintf(header_text, "%-24s    %s", "Device", "Index");
	sprintf(title_of_window, "%s contains...", raidrec->raid_device);
	newtPushHelpLine(help_text);
	for (b_res = (newtComponent) 12345; b_res != bOK && b_res != bCancel;) {
		headerMsg = newtLabel(1, 1, header_text);
		partitionsListbox =
			newtListbox(1, 2, 6, NEWT_FLAG_SCROLL | NEWT_FLAG_RETURNEXIT);
		redraw_disklist(disklist, keylist, partitionsListbox);
		i = 1;
		bAdd = newtCompactButton(i, 9, " Add ");
		bDelete = newtCompactButton(i += 8, 9, "Delete");
		bOK = newtCompactButton(i += 9, 9, "  OK  ");
		bCancel = newtCompactButton(i += 9, 9, "Cancel");
		newtOpenWindow(21, 7, 38, 10, title_of_window);
		myForm = newtForm(NULL, NULL, 0);
		if (disklist->entries == 0) {
			newtFormAddComponents(myForm, headerMsg, bAdd, bDelete, bOK,
								  bCancel, NULL);
		} else {
			newtFormAddComponents(myForm, headerMsg, partitionsListbox,
								  bAdd, bDelete, bOK, bCancel, NULL);
		}
		b_res = newtRunForm(myForm);
		if (b_res == bOK || b_res == bCancel) {	/* do nothing */
// That's OK. At the end of this subroutine (after this do/while loop),
// we'll throw away the changes if Cancel was pushed.
		} else {
			curr_choice = newtListboxGetCurrent(partitionsListbox);
			for (i = 0; i < disklist->entries && keylist[i] != curr_choice;
				 i++);
			if (i == disklist->entries && disklist->entries > 0) {
				log_to_screen("I don't know what that button does!");
			} else {
				currline = i;
				if (b_res == bAdd) {
					log_it("Making list of unallocated RAID slices");
					make_list_of_unallocated_raid_partitions
						(unallocated_raid_partitions, mountlist_dontedit,
						 raidlist);
					if (unallocated_raid_partitions->entries <= 0) {
						popup_and_OK
							("There are no unallocated partitions marked for RAID.");
					} else {
						log_it
							("Done. The user may add one or more of the above to RAID device");
						add_disklist_entry(disklist, raidrec->raid_device,
										   unallocated_raid_partitions);
						log_it("I have finished adding a disklist entry.");
						redraw_disklist(disklist, keylist,
										partitionsListbox);
					}
				} else if (b_res == bDelete) {
					delete_disklist_entry(disklist, raidrec->raid_device,
										  currline);
					redraw_disklist(disklist, keylist, partitionsListbox);
				} else {
					sprintf(tmp, "%s's index is %d. What should it be?",
							raidrec->raid_device,
							disklist->el[currline].index);
					sprintf(sz_res, "%d", disklist->el[currline].index);
					if (popup_and_get_string("Set index", tmp, sz_res, 10)) {
						disklist->el[currline].index = atoi(sz_res);
					}
					redraw_disklist(disklist, keylist, partitionsListbox);
				}
			}
		}
		newtFormDestroy(myForm);
		newtPopWindow();
	}
	newtPopHelpLine();
	if (b_res == bCancel) {
		memcpy((void *) raidlist, (void *) bkp_raidlist,
			   sizeof(struct raidlist_itself));
		memcpy((void *) raidrec, (void *) bkp_raidrec,
			   sizeof(struct raid_device_record));
		memcpy((void *) disklist, (void *) bkp_disklist,
			   sizeof(struct list_of_disks));
	}
	paranoid_free(tmp);
	paranoid_free(help_text);
	paranoid_free(title_of_window);
	paranoid_free(sz_res);
	paranoid_free(header_text);
	paranoid_free(bkp_raidrec);
	paranoid_free(bkp_disklist);
	paranoid_free(bkp_raidlist);
	paranoid_free(unallocated_raid_partitions);
}
#endif



/**
 * Ask the user which restore mode (nuke, interactive, or compare) we should use.
 * @return The mode selected: 'I' for interactive, 'N' for nuke, 'C' for compare,
 * or 'E' (or any other letter) for exit.
 */
char which_restore_mode()
{

  /** char *************************************************************/
	char output = '\0';
	char tmp[MAX_STR_LEN];

  /** newt *************************************************************/

	newtComponent b1;
	newtComponent b2;
	newtComponent b3;
	newtComponent b4;
	newtComponent b_res;
	newtComponent myForm;

	if (g_text_mode) {
		for (output = 'z'; !strchr("AICE", output); output = tmp[0]) {
			printf
				("Which mode - (A)utomatic, (I)nteractive, \n(C)ompare only, or (E)xit to shell?\n--> ");
			fgets(tmp, MAX_STR_LEN - 1, stdin);
		}
		return (output);
	}

	newtPushHelpLine
		("   Do you want to 'nuke' your system, restore interactively, or just compare?");
	newtOpenWindow(24, 3, 32, 17, "How should I restore?");
	b1 = newtButton(7, 1, "Automatically");
	b2 = newtButton(7, 5, "Interactively");
	b3 = newtButton(7, 9, "Compare only!");
	b4 = newtButton(7, 13, "Exit to shell");
	myForm = newtForm(NULL, NULL, 0);
	newtFormAddComponents(myForm, b1, b2, b3, b4, NULL);
	b_res = newtRunForm(myForm);
	newtFormDestroy(myForm);
	newtPopWindow();
	if (b_res == b1) {
		output = 'N';
	}
	if (b_res == b2) {
		output = 'I';
	}
	if (b_res == b3) {
		output = 'C';
	}
	if (b_res == b4) {
		output = 'E';
	}
	newtPopHelpLine();
	return (output);
}
