/*

    File: intrface.c

    Copyright (C) 1998-2007 Christophe GRENIER <grenier@cgsecurity.org>
  
    This software 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.
  
    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.
  
    You should have received a copy of the GNU General Public License along
    with this program; if not, write the Free Software Foundation, Inc., 51
    Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

 */
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
 
#include <stdarg.h>
#ifdef HAVE_STDLIB_H
#include <stdlib.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_STRING_H
#include <string.h>
#endif
#include <ctype.h>
#ifdef HAVE_TIME_H
#include <time.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include "types.h"
#include "common.h"
#include "lang.h"
#include "intrf.h"
#include "intrfn.h"
#include "intrface.h"
#include "godmode.h"
#include "fnctdsk.h"
#include "testdisk.h"
#include "adv.h"
#include "analyse.h"
#include "chgtype.h"
#include "edit.h"
#include "savehdr.h"
#include "dirpart.h"
#include "fat.h"
#include "partauto.h"
#include "log.h"

#define INTER_DISK_X	0
#define INTER_DISK_Y	7
extern const arch_fnct_t arch_i386;

static void interface_options(int *dump_ind, int *align, int *allow_partial_last_cylinder, unsigned int *expert, char**current_cmd);
static list_part_t *interface_load(disk_t *disk_car,list_part_t *list_part, const int debug);
static list_part_t *backup_merge(list_part_t *list_part,list_part_t *backup_part, const int debug);
static int write_MBR_code(disk_t *disk_car, char **current_cmd);
static int write_clean_table(disk_t *disk_car, char **current_cmd);
static int interface_check_disk_capacity(disk_t *disk_car);
static list_part_t *interface_analyse(disk_t *disk_car, const int debug, const int saveheader, char**current_cmd);
static int menu_disk(disk_t *disk_car, const int debug,int dump_ind, const int saveheader, char **current_cmd);

void interface_list(disk_t *disk_car, const int debug,const int test_recovery, const int saveheader, const int backup)
{
  list_part_t *list_part;
  log_info("\nAnalyse ");
  log_info("%s\n",disk_car->description(disk_car));
  printf("%s\n",disk_car->description(disk_car));
  printf(msg_PART_HEADER_LONG);
  list_part=disk_car->arch->read_part(disk_car,debug,saveheader);
  aff_buffer(BUFFER_WRITE,"Q");
  if(backup>0)
  {
    partition_save(disk_car,list_part,debug);
  }
  if(test_recovery>0)
  {
    list_part_t *element;
    log_info("rebuild_FAT_BS()\n");
    for(element=list_part;element!=NULL;element=element->next)
    {
      if(is_fat(element->part))
      {
	log_partition(disk_car,element->part);
	rebuild_FAT_BS(disk_car,element->part,debug,0,0,0,NULL);	/* dump_ind */
      }
      /* TODO ntfs */
    }
  }
  delete_list_part(list_part);
}

static int write_MBR_code(disk_t *disk_car, char **current_cmd)
{
  aff_copy(stdscr);
  wmove(stdscr,5,0);
  wdoprintf(stdscr,"%s\n",disk_car->description(disk_car));
  wmove(stdscr,INTER_DISK_Y,INTER_DISK_X);
  if(disk_car->arch->write_MBR_code==NULL)
  {
    display_message("Function to write a new MBR code not implemented for this partition type.\n");
    return 1;
  }
  wdoprintf(stdscr,msg_WRITE_MBR_CODE);
  if(ask_YN(stdscr)!=0 && ask_confirmation("Write a new copy of MBR code, confirm ? (Y/N)")!=0)
  {
    if(disk_car->arch->write_MBR_code(disk_car))
    {
      display_message("Write error: Can't write new MBR code.\n");
      return 2;
    }
    else
      display_message("A new copy of MBR code has been written.\nYou have to reboot for the change to take effect.\n");
  }
  return 0;
}

int write_clean_table(disk_t *disk_car, char**current_cmd)
{
  aff_copy(stdscr);
  wmove(stdscr,5,0);
  wdoprintf(stdscr,"%s\n",disk_car->description(disk_car));
  wmove(stdscr,INTER_DISK_Y,INTER_DISK_X);
  if(disk_car->arch->erase_list_part==NULL)
  {
    display_message("Clear partition table not implemented for this partition type.\n");
    return 1;
  }
  wdoprintf(stdscr,msg_WRITE_CLEAN_TABLE);
  if(ask_YN(stdscr)!=0 && ask_confirmation("Clear partition table, confirm ? (Y/N)")!=0)
  {
    if(disk_car->arch->erase_list_part(disk_car))
    {
      display_message("Write error: Can't clear partition table.\n");
      return 2;
    }
    else
      display_message("Partition table has been cleared.\nYou have to reboot for the change to take effect.\n");
  }
  return 0;
}

static int menu_disk(disk_t *disk_car, const int debug,int dump_ind, const int saveheader, char **current_cmd)
{
  int align=2;
  int allow_partial_last_cylinder=0;
  int ask_part_order=0;
  int command;
  int done=0;
  unsigned int menu=0;
  int real_key;
  unsigned int expert=0;
  char options[16];
  static struct MenuItem menuMain[]=
  {
	{'A',"Analyse","Analyse current partition structure and search for lost partitions"},
	{'T',"Advanced","Filesystem Utils"},
	{'G',"Geometry", "Change disk geometry" },
	{'O',"Options","Modify options"},
	{'C',"MBR Code","Write TestDisk MBR code to first sector"},
	{'D',"Delete","Delete all data in the partition table"},
	{'Q',"Quit","Return to disk selection"},
	{'E',"Editor","Basic disk editor"},
	{0,NULL,NULL}
  };
  strcpy(options, "AGOPTQ");
  if(disk_car->arch->write_MBR_code!=NULL)
    strcat(options,"C");
  if(disk_car->arch->erase_list_part!=NULL)
    strcat(options,"D");
  while(done==0)
  {
    aff_copy(stdscr);
    wmove(stdscr,5,0);
    wdoprintf(stdscr,"%s\n",disk_car->description(disk_car));
    wmove(stdscr,20,0);
    wdoprintf(stdscr,"Note: Correct disk geometry is required for a successful recovery. 'Analyse'");
    wmove(stdscr,21,0);
    wdoprintf(stdscr,"process may give some warnings if it thinks the logical geometry is mismatched.");
    if(*current_cmd!=NULL)
    {
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      if(strncmp(*current_cmd,"analyze",7)==0 || strncmp(*current_cmd,"analyse",7)==0)
      {
	(*current_cmd)+=7;
	command='A';
      }
      else if(strncmp(*current_cmd,"geometry",8)==0)
      {
	(*current_cmd)+=8;
	command='G';
      }
      else if(strncmp(*current_cmd,"advanced",8)==0)
      {
	(*current_cmd)+=8;
	command='T';
      }
      else if(strncmp(*current_cmd,"options",7)==0)
      {
	(*current_cmd)+=7;
	command='O';
      }
      else if(strncmp(*current_cmd,"delete",6)==0)
      {
	(*current_cmd)+=6;
	command='D';
      }
      else if(strncmp(*current_cmd,"mbr_code",8)==0)
      {
	(*current_cmd)+=8;
	command='C';
      }
      else
      {
	command='Q';
      }
    }
    else
      command = wmenuSelect_ext(stdscr,INTER_DISK_Y, INTER_DISK_X, menuMain, 10,
	    options, MENU_VERT | MENU_VERT_WARN | MENU_BUTTON | MENU_ACCEPT_OTHERS, &menu,&real_key);
    /* e for editor will be added when the editor will be better */
    switch(command)
    {
      case 'a':
      case 'A':
	{
	  int search_vista_part=0;
	  list_part_t *list_part;
	  list_part=interface_analyse(disk_car, debug, saveheader, current_cmd);
	  if(disk_car->arch==&arch_i386)
	  {
	    const int old_allow_partial_last_cylinder=allow_partial_last_cylinder;
	    const list_part_t *element;
	    for(element=list_part;element!=NULL;element=element->next)
	    {
	      if(element->part->part_offset%(2048*512)==0 && element->part->part_size%(2048*512)==0)
		search_vista_part=1;
	    }
	    if(search_vista_part==0)
	    {
	      if(*current_cmd!=NULL)
	      {
		while(*current_cmd[0]==',')
		  (*current_cmd)++;
		if(strncmp(*current_cmd,"mode_vista",10)==0)
		{
		  (*current_cmd)+=10;
		  search_vista_part=1;
		}
	      }
	      else
	      {
		log_info("Ask the user for vista mode\n");
		if(ask_confirmation("Should TestDisk search for partition created under Vista ? [Y/N] (answer Yes if unsure)")!=0)
		  search_vista_part=1;
	      }
	    }
	    if(search_vista_part==1)
	      allow_partial_last_cylinder=1;
	    if(old_allow_partial_last_cylinder!=allow_partial_last_cylinder)
	      hd_update_geometry(disk_car,allow_partial_last_cylinder,debug);
	    log_info("Allow partial last cylinder : %s\n", allow_partial_last_cylinder>0?"Yes":"No");
	    log_info("search_vista_part: %d\n", search_vista_part);
	  }
	  interface_recovery(disk_car, list_part, debug, dump_ind, align, ask_part_order, expert, search_vista_part, current_cmd);
	  delete_list_part(list_part);
	}
	break;
      case 'd':
      case 'D':
	write_clean_table(disk_car,current_cmd);
	break;
      case 'c':
      case 'C':
	write_MBR_code(disk_car,current_cmd);
	break;
      case 'g':
      case 'G':
	change_geometry(disk_car,current_cmd);
	break;
      case 'o':
      case 'O':
	{
	  const int old_allow_partial_last_cylinder=allow_partial_last_cylinder;
	  interface_options(&dump_ind, &align,&allow_partial_last_cylinder,&expert,current_cmd);
	  if(old_allow_partial_last_cylinder!=allow_partial_last_cylinder)
	    hd_update_geometry(disk_car,allow_partial_last_cylinder,debug);
	}
	break;
      case 't':
      case 'T':
	interface_adv(disk_car, debug, dump_ind, expert,current_cmd);
	break;
      case 'q':
      case 'Q':
	done = 1;
	break;
      case 'e':
      case 'E':
	interface_editor(disk_car);
	break;
    }
  }
  return 0;
}

int do_curses_testdisk(int debug,int dump_ind, const list_disk_t *list_disk, const int saveheader, const char *cmd_device, char **current_cmd)
{
  int command='Q';
  int allow_partial_last_cylinder=0;
  int done=0;
  unsigned int menu=0;
  int offset=0;
  int pos_num=0;
  const list_disk_t *element_disk;
  const list_disk_t *current_disk=NULL;
  static struct MenuItem menuMain[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'O',"Proceed",""},
    { 'Q',"Quit","Quit program"},
    { 0,NULL,NULL}
  };
  if(cmd_device!=NULL)
  {
    for(element_disk=list_disk;element_disk!=NULL;element_disk=element_disk->next)
    {
      if(strcmp(element_disk->disk->device,cmd_device)==0)
	current_disk=element_disk;
    }
  }
  else
    current_disk=list_disk;
  if(current_disk==NULL)
  {
    intrf_no_disk("TestDisk");
    return 0;
  }
    /* ncurses interface */
  while(done==0)
  {
    const char *options;
    int i;
    aff_copy(stdscr);
    wmove(stdscr,4,0);
    wdoprintf(stdscr,"  TestDisk is free software, and");
    wmove(stdscr,5,0);
    wdoprintf(stdscr,"comes with ABSOLUTELY NO WARRANTY.");
    wmove(stdscr,7,0);
    wdoprintf(stdscr,"Select a media (use Arrow keys, then press Enter):");
    /* TODO: Detect the LBA28 limitation */
    for(i=0,element_disk=list_disk;(element_disk!=NULL) && (i<offset);element_disk=element_disk->next,i++);
    for(;element_disk!=NULL && (i-offset)<10;i++,element_disk=element_disk->next)
    {
      wmove(stdscr,8+i-offset,0);
      if(element_disk!=current_disk)
	wdoprintf(stdscr,"%s\n",element_disk->disk->description_short(element_disk->disk));
      else
      {
	wattrset(stdscr, A_REVERSE);
	wdoprintf(stdscr,"%s\n",element_disk->disk->description_short(element_disk->disk));
	wattroff(stdscr, A_REVERSE);
      }
    }
    if(i<=10 && element_disk==NULL)
      options="OQ";
    else
      options="PNOQ";
    {
      int line=20;
#if defined(__CYGWIN__) || defined(__MINGW32__)
#else
#ifndef DJGPP
#ifdef HAVE_GETEUID
      if(geteuid()!=0)
      {
	wmove(stdscr,line++,0);
	wdoprintf(stdscr,"Note: Some disks won't appear unless you're root user.");
      }
#endif
#endif
#endif
      wmove(stdscr,line++,0);
      if(line==22)
	wdoprintf(stdscr,"Disk capacity must be correctly detected for a successful recovery.");
      else
	wdoprintf(stdscr,"Note: Disk capacity must be correctly detected for a successful recovery.");
      wmove(stdscr,line++,0);
      wdoprintf(stdscr,"If a disk listed above has incorrect size, check HD jumper settings, BIOS");
      wmove(stdscr,line++,0);
      wdoprintf(stdscr,"detection, and install the latest OS patches and disk drivers."); 
    }
    if(*current_cmd!=NULL)
    {
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      if(*current_cmd[0]=='\0')
	command='Q';
      else if(strncmp(*current_cmd,"inter",4)==0)
      {
	cmd_device=NULL;
	*current_cmd=NULL;
      }
      else
      {
	if(command=='O')
	  command='Q';
	else
	  command='O';
      }
    }
    if(*current_cmd==NULL)
      command = wmenuSelect_ext(stdscr,INTER_MAIN_Y, INTER_MAIN_X, menuMain, 8,
	    options, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, &menu,NULL);
    switch(command)
    {
      case KEY_UP:
      case 'P':
	if(current_disk->prev!=NULL)
	{
	  current_disk=current_disk->prev;
	  pos_num--;
	}
	if(pos_num<offset)
	  offset--;
	break;
      case KEY_DOWN:
      case 'N':
	if(current_disk->next!=NULL)
	{
	  current_disk=current_disk->next;
	  pos_num++;
	}
	if(pos_num>=offset+10)
	  offset++;
	break;
      case KEY_PPAGE:
	for(i=0;i<INTER_STRUCTURE && current_disk->prev!=NULL;i++)
	{
	  current_disk=current_disk->prev;
	  pos_num--;
	  if(pos_num<offset)
	    offset--;
	}
	break;
      case KEY_NPAGE:
	for(i=0;i<INTER_STRUCTURE && current_disk->next!=NULL;i++)
	{
	  current_disk=current_disk->next;
	  pos_num++;
	  if(pos_num>=offset+10)
	    offset++;
	}
	break;
      case 'o':
      case 'O':
	{
	  disk_t *disk=current_disk->disk;
	  autodetect_arch(disk);
	  if(interface_check_disk_capacity(disk)==0 &&
	      interface_partition_type(disk, allow_partial_last_cylinder, debug, current_cmd)==0)
	  {
	    if(menu_disk(disk, debug, dump_ind, saveheader, current_cmd))
	      done=1;
	  }
	}
	break;
      case 'q':
      case 'Q':
	done=1;
	break;
    }
  }
  return 0;
}

static int interface_check_disk_capacity(disk_t *disk_car)
{
  /* Test for LBA28 limitation */
  if(disk_car->CHS.sector>0 && (disk_car->CHS.cylinder+1) == (((1<<28)-1) / (disk_car->CHS.head+1) / disk_car->CHS.sector))
  {
    static const struct MenuItem menuMain[]=
    {
      { 'C', "Continue","The HD is really 137 GB only."},
      { 'Q',"Quit","The HD is bigger, it's safer to enable LBA48 support first."},
      { 0,NULL,NULL}
    };
    unsigned int menu=1;
    int car;
    log_warning("LBA28 limitation\n");
    aff_copy(stdscr);
    if(disk_car->arch->msg_part_type!=NULL)
      mvwaddstr(stdscr,22,0,disk_car->arch->msg_part_type);
    wmove(stdscr,4,0);
    wdoprintf(stdscr,"%s\n",disk_car->description(disk_car));
    wmove(stdscr,6,0);
    wdoprintf(stdscr,"The Harddisk size seems to be 137GB.");
    wmove(stdscr,7,0);
    wdoprintf(stdscr,"Support for 48-bit Logical Block Addressing (LBA) is needed to access");
    wmove(stdscr,8,0);
    wdoprintf(stdscr,"hard disks larger than 137 GB.");
    wmove(stdscr,9,0);
#if defined(__CYGWIN__) || defined(__MINGW32__)
    wdoprintf(stdscr,"Update Windows to support LBA48 (minimum: W2K SP4 or XP SP1)");
#endif
    car= wmenuSelect_ext(stdscr,INTER_MAIN_Y, INTER_MAIN_X, menuMain, 10,
	    "CQ", MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL);
    if(car=='c' || car=='C')
      return 0;
    return 1;
  }
  return 0;
}

static list_part_t *interface_analyse(disk_t *disk_car, const int debug, const int saveheader, char**current_cmd)
{
  list_part_t *list_part;
  int command;
  struct MenuItem menuAnalyse[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q',"Proceed","Try to locate partition"},
    { 'B', "Backup","Save current partition list to backup.log file and proceed"},
    { 0, NULL, NULL }
  };
  log_info("\nAnalyse ");
  log_info("%s\n",disk_car->description(disk_car));
  aff_buffer(BUFFER_RESET,"Q");
  /* ncurses interface */
  aff_copy(stdscr);
  if(disk_car->arch->msg_part_type!=NULL)
    mvwaddstr(stdscr,22,0,disk_car->arch->msg_part_type);
  wmove(stdscr,4,0);
  wdoprintf(stdscr,"%s\n",disk_car->description(disk_car));
  mvwaddstr(stdscr,5,0,"Checking current partition structure");
  wmove(stdscr,6,0);
  wrefresh(stdscr);
  wdoprintf(stdscr,msg_PART_HEADER_LONG);
  list_part=disk_car->arch->read_part(disk_car,debug,saveheader);
  wmove(stdscr,5,0);
  wclrtoeol(stdscr);	/* before addstr for BSD compatibility */
  waddstr(stdscr,"Current partition structure:");
  log_info("Current partition structure:\n");
  if(*current_cmd!=NULL)
  {
    command='Q';
    while(*current_cmd[0]==',')
      (*current_cmd)++;
    if(strncmp(*current_cmd,"backup",6)==0)
    {
      (*current_cmd)+=6;
      if(list_part!=NULL)
	command='B';
    }
    screen_buffer_to_log();
  }
  else
    command=screen_buffer_display(stdscr,(list_part!=NULL?"QB":"Q"),menuAnalyse);
  if(command=='B')
  {
    log_info("Backup partition structure\n");
    if(partition_save(disk_car,list_part,debug)<0)
    {
      display_message("Can't create backup.log.\n");
    }
  }
  return list_part;
}

int interface_write(disk_t *disk_car,list_part_t *list_part,const int can_search_deeper, const int can_ask_minmax_ext, int *no_confirm, char **current_cmd, unsigned int *menu)
{
  list_part_t *parts;
  struct MenuItem menuWrite[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q', "Quit","Return to main menu"},
    { 'S', "Search!","Search deeper, try to find more partitions"},
    { 'W', "Write","Write partition structure to disk"},
    { 'E', "Extd Part","Maximize/Minimize extended partition"},
    { 0, NULL, NULL }
  };
  int command;
  log_info("\ninterface_write()\n");
  aff_buffer(BUFFER_RESET,"Q");
  aff_copy(stdscr);
  wmove(stdscr,4,0);
  wdoprintf(stdscr,"%s",disk_car->description(disk_car));
  wmove(stdscr,5,0);
  mvwaddstr(stdscr,6,0,msg_PART_HEADER_LONG);
  for(parts=list_part;parts!=NULL;parts=parts->next)
    if(parts->part->status!=STATUS_LOG)
      aff_part_buffer(AFF_PART_ORDER,disk_car,parts->part);
  for(parts=list_part;parts!=NULL;parts=parts->next)
    if(parts->part->status==STATUS_LOG)
      aff_part_buffer(AFF_PART_ORDER,disk_car,parts->part);
  if(list_part==NULL)
  {
    aff_buffer(BUFFER_ADD," \nNo partition found or selected for recovery");
    if(*current_cmd!=NULL)
    {
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      if(strncmp(*current_cmd,"search",6)==0)
      {
	(*current_cmd)+=6;
	command='S';
      }
      else
	command='Q';
      screen_buffer_to_log();
    }
    else
      command=screen_buffer_display_ext(stdscr,(can_search_deeper?"S":""),menuWrite,menu);
  }
  else
  {
    if(*current_cmd!=NULL)
    {
      do
      {
	while(*current_cmd[0]==',')
	  (*current_cmd)++;
	command='Q';
	if(strncmp(*current_cmd,"search",6)==0)
	{
	  (*current_cmd)+=6;
	  if(can_search_deeper)
	    command='S';
	}
	else if(strncmp(*current_cmd,"noconfirm",9)==0)
	{
	  command=0;	/* do nothing */
	  (*no_confirm)=1;
	  (*current_cmd)+=9;
	}
	else if(strncmp(*current_cmd,"write",5)==0)
	{
	  (*current_cmd)+=5;
	  if(disk_car->arch->write_part!=NULL)
	    command='W';
	}
      } while(command==0);
      screen_buffer_to_log();
    }
    else
    {
      char options[10];
      options[0]=0;
      if(can_search_deeper)
	strcat(options,"S");
      if(disk_car->arch->write_part!=NULL)
	strcat(options,"W");
      else
	aff_buffer(BUFFER_ADD," \nWrite isn't available because the partition table type \"%s\" has been selected.",
	    disk_car->arch->part_name);
      if(can_ask_minmax_ext)
	strcat(options,"E");
      command=screen_buffer_display_ext(stdscr,options,menuWrite,menu);
    }
  }
  return command;
}

static void interface_options(int *dump_ind, int *align, int *allow_partial_last_cylinder, unsigned int *expert, char**current_cmd)
{
  /* paranoid: search in partition
   * dump_ind dump sector Y/N
   * */
  if(*current_cmd!=NULL)
  {
    int keep_asking=1;
    do
    {
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      keep_asking=0;
    } while(keep_asking>0);
  }
  else
  {
    int done = FALSE;
    unsigned int menu = 4;
    /* ncurses interface */
    while (done==FALSE)
    {
      int car;
      int real_key;
      struct MenuItem menuOptions[]=
      {
	{ 'E',NULL,"Expert mode adds some functionalities"},
	{ 'C',NULL,"Partitions are aligned on cylinder/head boundaries" },
	{ 'A',NULL,""},
	{ 'D',NULL,"Dump essential sectors" },
	{ 'Q',"[ Ok ]","Done with changing options"},
	{ 0, NULL, NULL }
      };
      menuOptions[0].name=*expert?"Expert mode : Yes":"Expert mode : No";
      switch(*align)
      {
	case 0:
	  menuOptions[1].name="Cylinder boundary : No";
	  break;
	case 1:
	  menuOptions[1].name="Cylinder boundary : Head boundary only";
	  break;
	case 2:
	  menuOptions[1].name="Cylinder boundary : Yes";
	  break;
      }
      menuOptions[2].name=*allow_partial_last_cylinder?"Allow partial last cylinder : Yes":"Allow partial last cylinder : No";
      menuOptions[3].name=*dump_ind?"Dump : Yes":"Dump : No";
      aff_copy(stdscr);

      car=wmenuSelect_ext(stdscr,INTER_OPTION_Y, INTER_OPTION_X, menuOptions, 0, "ECADQ", MENU_VERT|MENU_VERT_ARROW2VALID, &menu,&real_key);
      switch(car)
      {
	case 'd':
	case 'D':
	  *dump_ind=!*dump_ind;
	  break;
	case 'c':
	case 'C':
	  if(*align<2)
	    (*align)++;
	  else
	    *align=0;
	  break;
	case 'a':
	case 'A':
	  *allow_partial_last_cylinder=!*allow_partial_last_cylinder;
	  break;
	case 'e':
	case 'E':
	  *expert=!*expert;
	  break;
	case key_ESC:
	case 'q':
	case 'Q':
	  done = TRUE;
	  break;
      }
    }
  }
  /* write new options to log file */
  log_info("New options :\n Dump : %s\n ",
      *dump_ind?"Yes":"No");
  switch(*align)
  {
    case 0:
      log_info("Cylinder boundary : No");
      break;
    case 1:
      log_info("Cylinder boundary : Head boundary only");
      break;
    case 2:
      log_info("Cylinder boundary : Yes");
      break;
  }
  log_info("\n Allow partial last cylinder : %s\n Expert mode : %s\n",
      *allow_partial_last_cylinder?"Yes":"No",
      *expert?"Yes":"No");
}

list_part_t *ask_structure(disk_t *disk_car,list_part_t *list_part, const int debug, char **current_cmd)
{
  int quit=0;
  int offset=0;
  int pos_num=0;
  list_part_t *pos=list_part;
  int rewrite=1;
  do
  {
    int i;
    int command;
    list_part_t *parts;
    int structure_status;
    if(rewrite)
    {
      aff_copy(stdscr);
      wmove(stdscr,4,0);
      wdoprintf(stdscr,"%s",disk_car->description(disk_car));
      mvwaddstr(stdscr,5,0,msg_PART_HEADER);
      rewrite=0;
    }
    structure_status=disk_car->arch->test_structure(list_part);
    for(i=0,parts=list_part;(parts!=NULL) && (i<offset);parts=parts->next,i++);
    for(i=offset;(parts!=NULL) &&((i-offset)<INTER_STRUCTURE);i++,parts=parts->next)
    {
      wmove(stdscr,6+i-offset,0);
      wclrtoeol(stdscr);	/* before addstr for BSD compatibility */
      if(parts==pos)
      {
	wattrset(stdscr, A_REVERSE);
      }
      if(structure_status==0 && parts->part->status!=STATUS_DELETED && has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(2));
      aff_part(stdscr,AFF_PART_NONL,disk_car,parts->part);
      if(structure_status==0 && parts->part->status!=STATUS_DELETED && has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      if(parts==pos)
      {
	char buffer_part_size[100];
	wattroff(stdscr, A_REVERSE);
	wmove(stdscr,24,0);
	wclrtoeol(stdscr);	/* before addstr for BSD compatibility */
	if(parts->part->info[0]!='\0')
	{
	  wdoprintf(stdscr,"%s, ",parts->part->info);
	}
	wdoprintf(stdscr,"%s",size_to_unit(parts->part->part_size,buffer_part_size));
      }
    }
    if(structure_status==0)
      mvwaddstr(stdscr,19,0,msg_STRUCT_OK);
    else
    {
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(1));
      mvwaddstr(stdscr,19,0,msg_STRUCT_BAD);
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
    }
    if(list_part!=NULL && disk_car->arch->msg_part_type!=NULL)
    {
      mvwaddstr(stdscr,19,16,"Use ");
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
      waddstr(stdscr,"Up");
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      waddstr(stdscr,"/");
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
      waddstr(stdscr,"Down");
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      waddstr(stdscr," Arrow keys to select partition.");
      mvwaddstr(stdscr,20,0,"Use ");
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
      waddstr(stdscr,"Left");
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      waddstr(stdscr,"/");
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
      waddstr(stdscr,"Right");
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      waddstr(stdscr," Arrow keys to CHANGE partition characteristics:");
      mvwaddstr(stdscr,21,0,disk_car->arch->msg_part_type);
    }
    wmove(stdscr,22,0);
    wclrtoeol(stdscr);	/* before addstr for BSD compatibility */
    waddstr(stdscr,"Keys ");
    if(has_colors())
      wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
    waddstr(stdscr,"A");
    if(has_colors())
      wbkgdset(stdscr,' ' | COLOR_PAIR(0));
    waddstr(stdscr,": add partition, ");
    if(has_colors())
      wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
    waddstr(stdscr,"L");
    if(has_colors())
      wbkgdset(stdscr,' ' | COLOR_PAIR(0));
    waddstr(stdscr,": load backup, ");

    if(list_part==NULL)
    {
      waddstr(stdscr,"Enter: to continue");
    }
    else
    {
      if(pos->part->arch==NULL || pos->part->arch==disk_car->arch)
      {
	if(has_colors())
	  wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
	waddstr(stdscr,"T");
	if(has_colors())
	  wbkgdset(stdscr,' ' | COLOR_PAIR(0));
	waddstr(stdscr,": change type, ");
      }
      switch(pos->part->upart_type)
      {
	case UP_EXT2:
	case UP_EXT3:
	case UP_RFS:
	case UP_RFS2:
	case UP_RFS3:
	case UP_FAT12:
	case UP_FAT16:
	case UP_FAT32:
	case UP_NTFS:
	  if(has_colors())
	    wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
	  waddstr(stdscr,"P");
	  if(has_colors())
	    wbkgdset(stdscr,' ' | COLOR_PAIR(0));
	  waddstr(stdscr,": list files, ");
	  break;
	default:
	  break;
      }
      if(has_colors())
	wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
      mvwaddstr(stdscr,23,5, "Enter");
      if(has_colors())
	wbkgdset(stdscr,' ' | COLOR_PAIR(0));
      waddstr(stdscr,": to continue");
    }
    wrefresh(stdscr);
    if(*current_cmd!=NULL)
    {
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      if(strncmp(*current_cmd,"list",4)==0)
      {
	(*current_cmd)+=4;
	command='p';
      }
      else
	command='q';
    }
    else
      command=wgetch(stdscr);
    switch(command)
    {
      case 'q':
      case '\r':
      case '\n':
      case KEY_ENTER:
#ifdef PADENTER
      case PADENTER:
#endif
      case 'M':
	quit=1;
	break;
      case KEY_UP:
	if(list_part!=NULL)
	{
	  only_one_bootable(list_part,pos);
	  if(pos->prev!=NULL)
	  {
	    pos=pos->prev;
	    pos_num--;
	  }
	  if(pos_num<offset)
	    offset--;
	}
	break;
      case KEY_DOWN:
	if(list_part!=NULL)
	{
	  only_one_bootable(list_part,pos);
	  if(pos->next!=NULL)
	  {
	    pos=pos->next;
	    pos_num++;
	  }
	  if(pos_num>=offset+INTER_STRUCTURE)
	    offset++;
	}
	break;
      case KEY_PPAGE:
	if(list_part!=NULL)
	{
	  only_one_bootable(list_part,pos);
	  for(i=0;(i<INTER_STRUCTURE) && (pos->prev!=NULL);i++)
	  {
	    pos=pos->prev;
	    pos_num--;
	    if(pos_num<offset)
	      offset--;
	  }
	}
	break;
      case KEY_NPAGE:
	if(list_part!=NULL)
	{
	  only_one_bootable(list_part,pos);
	  for(i=0;(i<INTER_STRUCTURE) && (pos->next!=NULL);i++)
	  {
	    pos=pos->next;
	    pos_num++;
	    if(pos_num>=offset+INTER_STRUCTURE)
	      offset++;
	  }
	}
	break;
      case KEY_RIGHT:
      case '+':
      case ' ':
	if(list_part!=NULL)
	{
	  if(pos->part->arch==NULL || pos->part->arch==disk_car->arch)
	    disk_car->arch->set_next_status(disk_car,pos->part);
	}
	break;
      case KEY_LEFT:
      case '-':
	if(list_part!=NULL)
	{
	  if(pos->part->arch==NULL || pos->part->arch==disk_car->arch)
	    disk_car->arch->set_prev_status(disk_car,pos->part);
	}
	break;
      case 'a':
      case 'A':
	{
	  list_part=disk_car->arch->add_partition(disk_car,list_part, debug,current_cmd);
	  rewrite=1;
	  offset=0;
	  pos_num=0;
	  pos=list_part;
	}
	break;
      case 't':
      case 'T':
	if(list_part!=NULL)
	{
	  if(pos->part->arch==NULL || pos->part->arch==disk_car->arch)
	  {
	    rewrite=1;
	    change_part_type(disk_car,pos->part,current_cmd);
	  }
	}
	break;
      case 'p':
      case 'P':
	if(list_part!=NULL)
	  dir_partition(disk_car,pos->part,debug,current_cmd);
	break;
      case 'l':
      case 'L':
        list_part=interface_load(disk_car,list_part,debug);
	rewrite=1;
	offset=0;
	pos_num=0;
	pos=list_part;
        break;
      default:
/*	log_trace("ask_structure command=%x\n",command); */
	break;
    }
  } while(quit==0);
  return list_part;
}

static list_part_t *backup_merge(list_part_t *list_part,list_part_t *backup_part, const int debug)
{
  list_part_t *partition;
  for(partition=backup_part;partition!=NULL;partition=partition->next)
  {
    partition_t *new_partition=partition_new();
    dup_partition_t(new_partition,partition->part);
    list_part=insert_new_partition(list_part, new_partition);
  }
  return list_part;
}

static list_part_t *interface_load(disk_t *disk_car,list_part_t *list_part, const int debug)
{
  struct list_head *backup_walker=NULL;
  backup_disk_t *backup_list=partition_load(disk_car,debug);
  {
    log_info("interface_load\n");
    list_for_each(backup_walker,&backup_list->list)
    {
      list_part_t *element;
      backup_disk_t *backup;
      backup=list_entry(backup_walker, backup_disk_t, list);
      log_info("%s %s",backup->description,ctime(&backup->my_time));
      for(element=backup->list_part;element!=NULL;element=element->next)
	log_partition(disk_car,element->part);
    }
  }
  {
    int quit=0;
    int offset=0;
    int backup_current_num=0;
    int rewrite=1;
    unsigned int menu=3;   /* default : quit */
    struct list_head *backup_current=backup_list->list.next;
    struct MenuItem menuLoadBackup[]=
    {
      { 'P', "Previous",""},
      { 'N', "Next","" },
      { 'L',"Load","Load partition structure from backup and try to locate partition"},
      { 'Q',"Cancel","Don't use backup and try to locate partition"},
      { 0, NULL, NULL }
    };
    char options[20];
    do
    {
      int i;
      if(rewrite)
      {
	aff_copy(stdscr);
	mvwaddstr(stdscr,4,0,disk_car->description(disk_car));
	if(backup_list!=NULL)
	{
	  mvwaddstr(stdscr,5,0,"Choose the backup you want to restore:");
	  mvwaddstr(stdscr,20,0,"PS: Don't worry you will have to confirm the partition restoration.");
	}
	else
	{
	  mvwaddstr(stdscr,5,0,"No backup found!");
	}
	rewrite=0;
      }
      if(backup_list!=NULL)
      {
	backup_disk_t *backup=NULL;
	for(i=0,backup_walker=backup_list->list.next;(backup_walker!=&backup_list->list) && (i<offset);backup_walker=backup_walker->next,i++);
	for(i=offset;(backup_walker!=&backup_list->list) &&((i-offset)<INTER_STRUCTURE);i++,backup_walker=backup_walker->next)
	{
	  backup=list_entry(backup_walker, backup_disk_t, list);
	  wmove(stdscr,8+i-offset,0);
	  wclrtoeol(stdscr);	/* before addstr for BSD compatibility */
	  if(backup_walker==backup_current)
	  {
	    wattrset(stdscr, A_REVERSE);
	    wdoprintf(stdscr,"%s %s",backup->description,ctime(&backup->my_time));
	    wattroff(stdscr, A_REVERSE);
	  } else
	  {
	    wdoprintf(stdscr,"%s %s",backup->description,ctime(&backup->my_time));
	  }
	}
	if(i<=INTER_STRUCTURE && backup==NULL)
	{
	  strncpy(options,"LQ",sizeof(options));
	  menu=0;
	}
	else
	{
	  strncpy(options,"PNLQ",sizeof(options));
	  menu=2;
	}
      }
      else
      {
	menu=0;
	strncpy(options,"Q",sizeof(options));
      }
      switch(wmenuSelect(stdscr,INTER_DUMP_Y,INTER_DUMP_X, menuLoadBackup, 8, options, MENU_HORIZ| MENU_BUTTON | MENU_ACCEPT_OTHERS, menu))
      {
	case 'q':
	case 'Q':
	  quit=1;
	  break;
	case 'l':
	case 'L':
	  quit=2;
	  break;
	case KEY_UP:
	  if(backup_current->prev!=&backup_list->list)
	  {
	    backup_current=backup_current->prev;
	    backup_current_num--;
	    if(backup_current_num<offset)
	      offset--;
	  }
	  break;
	case KEY_DOWN:
	  if(backup_current->next!=&backup_list->list)
	  {
	    backup_current=backup_current->next;
	    backup_current_num++;
	    if(backup_current_num>=offset+INTER_STRUCTURE)
	      offset++;
	  }
	  break;
	case KEY_PPAGE:
	  {
	    for(i=0;(i<INTER_STRUCTURE) && (backup_current->prev!=&backup_list->list);i++)
	    {
	      backup_current=backup_current->prev;
	      backup_current_num--;
	      if(backup_current_num<offset)
		offset--;
	    }
	  }
	  break;
	case KEY_NPAGE:
	  {
	    for(i=0;(i<INTER_STRUCTURE) && (backup_current->next!=&backup_list->list);i++)
	    {
	      backup_current=backup_current->next;
	      backup_current_num++;
	      if(backup_current_num>=offset+INTER_STRUCTURE)
		offset++;
	    }
	  }
	  break;
	default:
	  /*	log_trace("ask_structure car=%x\n",car); */
	  break;
      }
    } while(quit==0);
    if(backup_current!=NULL && quit==2)
    {
      list_part_t *partition;
      backup_disk_t *backup;
      backup=list_entry(backup_current, backup_disk_t, list);
      for(partition=backup->list_part;partition!=NULL;partition=partition->next)
      {
	/* Check partition and load partition name */
	disk_car->arch->check_part(disk_car,debug,partition->part,0);
      }
      list_part=backup_merge(list_part,backup->list_part,debug);
    }
  }
  { /* Cleanup */
    struct list_head *backup_walker_next = NULL;
    list_for_each_safe(backup_walker,backup_walker_next,&backup_list->list)
    {
      backup_disk_t *backup;
      backup=list_entry(backup_walker, backup_disk_t, list);
      delete_list_part(backup->list_part);
      free(backup);
    }
    free(backup_list);
  }
  return list_part;
}

int interface_superblock(disk_t *disk_car,list_part_t *list_part, char**current_cmd)
{
  list_part_t *parts;
  partition_t *old_part=NULL;
  struct MenuItem menuSuperblock[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q',"Quit","Return to Advanced menu"},
    { 0, NULL, NULL }
  };
  aff_buffer(BUFFER_RESET,"Q");
  aff_copy(stdscr);
  wmove(stdscr,4,0);
  wdoprintf(stdscr,"%s",disk_car->description(disk_car));
  wmove(stdscr,5,0);
  mvwaddstr(stdscr,6,0,msg_PART_HEADER_LONG);
  for(parts=list_part;parts!=NULL;parts=parts->next)
  {
    if(old_part==NULL || (old_part->part_offset!=parts->part->part_offset) ||
      old_part->part_size!=parts->part->part_size ||
      old_part->part_type_i386!=parts->part->part_type_i386 ||
      old_part->part_type_sun!=parts->part->part_type_sun ||
      old_part->part_type_mac!=parts->part->part_type_mac ||
      old_part->upart_type!=parts->part->upart_type)
    {
      aff_part_buffer(AFF_PART_SHORT,disk_car,parts->part);
      old_part=parts->part;
    }
    aff_buffer(BUFFER_ADD,"superblock %u, blocksize=%u\n",parts->part->boot_sector,parts->part->blocksize);
  }
  if(*current_cmd!=NULL)
  {
    screen_buffer_to_log();
    return 0;
  }
  return screen_buffer_display(stdscr,"",menuSuperblock);
}



syntax highlighted by Code2HTML, v. 0.9.1