/*

    File: intrf.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_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_SELECT_H
#include <sys/select.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#ifdef HAVE_SYS_CYGWIN_H
#include <sys/cygwin.h>
#endif
#ifdef HAVE_LIBGEN_H
#include <libgen.h>
#endif
#ifdef HAVE_DIRENT_H
#include <dirent.h>
#endif
#include <errno.h>
#include "types.h"
#include "common.h"
#include "lang.h"
#include "intrf.h"
#include "intrfn.h"
#include "fnctdsk.h"
#include "dir.h"
#include "log.h"

/* Use COLS (actual number of columns) or COLUMNS (number of columns the program has been designed for) ? */

#define GS_DEFAULT -1
#define GS_key_ESCAPE -2
extern const arch_fnct_t arch_i386;
extern const arch_fnct_t arch_mac;
extern const arch_fnct_t arch_none;
extern const arch_fnct_t arch_sun;
extern const arch_fnct_t arch_xbox;
extern const char *monstr[];

static void set_parent_directory(char *dst_directory);
static void dir_aff_entry(WINDOW *window, struct file_info *dir_info);

int get_string(char *str, int len, char *def)
{
    int c;
    int i = 0;
    int x, y;
    int use_def = FALSE;
    curs_set(1);
    getyx(stdscr, y, x);
    wclrtoeol(stdscr);
    str[0] = 0;

    if (def != NULL) {
	mvwaddstr(stdscr,y, x, def);
	wmove(stdscr,y, x);
	use_def = TRUE;
    }

    wrefresh(stdscr);
    while ((c = wgetch(stdscr)) != '\n' && c != key_CR
#ifdef PADENTER
      && c!= PADENTER
#endif
      )
      {
	switch (c) {
	  /* escape is generated by enter from keypad */
	  /*
	     case key_ESC:
	     wmove(stdscr,y, x);
	     wclrtoeol(stdscr);
	     curs_set(0);
	     wrefresh(stdscr);
	     return GS_key_ESCAPE;
	   */
	  case KEY_DC:
	  case KEY_BACKSPACE:
	    if (i > 0) {
	      str[--i] = 0;
	      mvaddch(y, x+i, ' ');
	      wmove(stdscr,y, x+i);
	    } else if (use_def) {
	      wclrtoeol(stdscr);
	      use_def = FALSE;
	    }
	    break;
	  default:
	    if (i < len && isprint(c)) {
	      mvaddch(y, x+i, c);
	      if (use_def) {
		wclrtoeol(stdscr);
		use_def = FALSE;
	      }
	      str[i++] = c;
	      str[i] = 0;
	    }
	}
	wrefresh(stdscr);
    }
    curs_set(0);
    wrefresh(stdscr);
    if (use_def)
	return GS_DEFAULT;
    else
	return i;
}

int wgetch_nodelay(WINDOW *window)
{
  int res;
  nodelay(window,TRUE);
  res=wgetch(window);
  nodelay(window,FALSE);
  return res;
}

/*
 * Actual function which prints the button bar and highlights the active button
 * Should not be called directly. Call function menuSelect instead.
 */

int wmenuUpdate(WINDOW *window, int y, int x, const struct MenuItem *menuItems, const unsigned int itemLength, const char *available, const int menuType, unsigned int current)
{
    unsigned int i, lmargin = x, ymargin = y;
    const char *mcd;
    unsigned int lenNameMax=0;
    for( i = 0; menuItems[i].key!=0; i++ )
      if(strchr(available, menuItems[i].key)!=NULL )
      {
	unsigned int lenName = strlen( menuItems[i].name );
	if(lenNameMax<lenName && lenName < itemLength)
	  lenNameMax=lenName;
      }
    /* Print available buttons */
    for( i = 0; menuItems[i].key!=0; i++ )
    {
      char buff[80];
      unsigned int lenName;
      const char *mi;
      wmove(window, y, x ); wclrtoeol(window);

	/* Search next available button */
	while( menuItems[i].key!=0 && strchr(available, menuItems[i].key)==NULL )
	{
	    i++;
	}
	if( menuItems[i].key==0 ) break; /* No more menu items */

	/* If selected item is not available and we have bypassed it,
	   make current item selected */
	if( current < i && menuItems[current].key < 0 ) current = i;

	/* If current item is selected, highlight it */
	if( current == i )
	{
	  wattrset(window, A_REVERSE);
	}

	/* Print item */
	mi = menuItems[i].name;
	lenName = strlen( mi );
	if(lenName>=sizeof(buff))
	{
	  log_critical("\nBUG: %s\n",mi);
	}
	if(lenName > itemLength)
	{
	  if( menuType & MENU_BUTTON )
		snprintf(buff, sizeof(buff),"[%s]",mi);
	  else
		snprintf(buff, sizeof(buff),"%s",mi);
	}
	else
	{
	  if( menuType & MENU_BUTTON )
	  {
	    if(menuType & MENU_VERT)
		snprintf( buff, sizeof(buff),"[%*s%-*s]", (itemLength - lenNameMax) / 2, "",
			(itemLength - lenNameMax + 1) / 2 + lenNameMax, mi );
	    else
		snprintf( buff, sizeof(buff),"[%*s%-*s]", (itemLength - lenName) / 2, "",
			(itemLength - lenName + 1) / 2 + lenName, mi );
	  }
	  else
		snprintf( buff, sizeof(buff),"%*s%-*s", (itemLength - lenName) / 2, "",
			(itemLength - lenName + 1) / 2 + lenName, mi );
	}
	mvwaddstr(window, y, x, buff );

	/* Lowlight after selected item */
	if( current == i )
	{
	  wattroff(window, A_REVERSE);
	}
	mcd = menuItems[i].desc;
	if(menuType & MENU_VERT_WARN)
	  mvwaddstr(window, y, x+itemLength+4, mcd );

	/* Calculate position for the next item */
	if( menuType & MENU_VERT )
	{
	    y += 1;
	    if( y >= WARNING_START )
	    {
		y = ymargin;
		x += itemLength + MENU_SPACING;
		if( menuType & MENU_BUTTON ) x += 2;
	    }
	}
	else
	{
	    x += itemLength + MENU_SPACING;
	    if( menuType & MENU_BUTTON ) x += 2;
	    if( x > COLUMNS - lmargin - 12 )
	    {
		x = lmargin;
		y ++ ;
	    }
	}
    }
    /* Print the description of selected item */
    mcd = menuItems[current].desc;
    if(!(menuType & MENU_VERT_WARN))
      mvwaddstr(window, WARNING_START + 1, (COLUMNS - strlen( mcd )) / 2, mcd );
    return y;
}

/* This function takes a list of menu items, lets the user choose one *
 * and returns the value keyboard shortcut of the selected menu item  */

int wmenuSelect(WINDOW *window, int y, int x, const struct MenuItem *menuItems, const unsigned int itemLength, const char *available, int menuType, unsigned int menuDefault)
{
  unsigned int current=menuDefault;
  return wmenuSelect_ext(window, y, x, menuItems, itemLength, available, menuType, &current,NULL);
}

int wmenuSelect_ext(WINDOW *window, int y, int x, const struct MenuItem *menuItems, const unsigned int itemLength, const char *available, int menuType, unsigned int *current, int *real_key)
{
    int i, ylast = y, key = 0;
    /*
    if( ( menuType & ( MENU_HORIZ | MENU_VERT ) )==0 )    
    {
	wdoprintf(window,"Menu without direction. Defaulting horizontal.");
	menuType |= MENU_HORIZ;
    }
    */
    /* Warning: current may be out of bound, not checked */
    /* Make sure that the current is one of the available items */
    while(strchr(available, menuItems[*current].key)==NULL)
    {
	(*current)++ ;
	if( menuItems[*current].key==0 )
	{
	  *current = 0;
	}
    }
    /* Repeat until allowable choice has been made */
    while( key==0 )
    {
	/* Display the menu */
	ylast = wmenuUpdate( window, y, x, menuItems, itemLength, available,
			    menuType, *current );
	wrefresh(window);
	/* Don't put wgetch after the following wclrtoeol */
	key = wgetch(window);
	if(real_key!=NULL)
	  *real_key=key;

	/* Clear out all prompts and such */
	for( i = y; i < ylast; i ++ )
	{
	    wmove(window, i, x );
	    wclrtoeol(window);
	}
	wmove(window, WARNING_START + 1, 0 );
	wclrtoeol(window);
	/* Cursor keys */
	switch(key)
	{
	  case KEY_UP:
	    if( (menuType & MENU_VERT)!=0 )
	    {
	      do {
		if( (*current)-- == 0 )
		{
		  while( menuItems[(*current)+1].key ) (*current) ++ ;
		}
	      } while( strchr( available, menuItems[*current].key )==NULL );
	      key = 0;
	    }
	    break;
	  case KEY_DOWN:
	    if( (menuType & MENU_VERT)!=0 )
	    {
	      do {
		(*current) ++ ;
		if( menuItems[*current].key==0 ) *current = 0 ;
	      } while( strchr( available, menuItems[*current].key )==NULL );
	      key = 0;
	    }
	    break;
	  case KEY_RIGHT:
	    if( (menuType & MENU_HORIZ)!=0 )
	    {
	      do {
		(*current) ++ ;
		if( menuItems[*current].key==0 ) 
		{
		  *current = 0 ;
		}
	      } while( strchr( available, menuItems[*current].key )==NULL );
	      key = 0;
	    }
	    break;
	  case KEY_LEFT:
	    if( (menuType & MENU_HORIZ) !=0)
	    {
	      do {
		if( (*current)-- == 0 )
		{
		  while( menuItems[(*current) + 1].key ) (*current) ++ ;
		}
	      } while( strchr( available, menuItems[*current].key )==NULL );
	      key = 0;
	    }
	    break;
	}
	/* Enter equals to the keyboard shortcut of current menu item */
	if((key==13) || (key==10) || (key==KEY_ENTER) ||
	    (((menuType & MENU_VERT) != 0) && ((menuType & MENU_VERT_ARROW2VALID) != 0)
	     && (key==KEY_RIGHT || key==KEY_LEFT)))
	    key = menuItems[*current].key;
#ifdef PADENTER
	if(key==PADENTER)
	  key = menuItems[*current].key;
#endif

	/* Is pressed key among acceptable ones */
	if( key!=0 && (strchr(available, toupper(key))!=NULL || strchr(available, key)!=NULL))
	    break;
	/* Should all keys to be accepted? */
	if( key && (menuType & MENU_ACCEPT_OTHERS)!=0 ) break;
	/* The key has not been accepted so far -> let's reject it */
#ifdef DEBUG
	if( key )
	{
		wmove(window,5,0);
		wdoprintf(window,"key %03X",key);
	    putchar( BELL );
	}
#endif
	key = 0;
    }
    /* Clear out prompts and such */
    for( i = y; i <= ylast; i ++ )
    {
	wmove(window, i, x );
	wclrtoeol(window);
    }
    wmove(window, WARNING_START + 1, 0 );
    wclrtoeol(window);
    return key;
}

/* Function menuSelect takes way too many parameters  *
 * Luckily, most of time we can do with this function */

int wmenuSimple(WINDOW *window,const struct MenuItem *menuItems, unsigned int menuDefault)
{
    unsigned int i, j, itemLength = 0;
    char available[MENU_MAX_ITEMS];

    for(i = 0; menuItems[i].key; i++)
    {
	j = strlen(menuItems[i].name);
	if( j > itemLength ) itemLength = j;
	available[i] = menuItems[i].key;
    }
    available[i] = 0;
    return wmenuSelect(window,18, 0, menuItems, itemLength, available, MENU_HORIZ | MENU_BUTTON, menuDefault);
}

/* End of command menu support code */

unsigned long long int ask_number(const unsigned long long int val_cur, const unsigned long long int val_min, const unsigned long long int val_max, const char * _format, ...)
{
  char res[200];
  char res2[200];
  char response[LINE_LENGTH];
  char def[LINE_LENGTH];
  unsigned long int tmp_val;
  va_list ap;
  va_start(ap,_format);
  vsnprintf(res,sizeof(res),_format,ap);
  if(val_min!=val_max)
    snprintf(res2,sizeof(res2),"(%llu-%llu) :",val_min,val_max);
  else
    res2[0]='\0';
  va_end(ap);
  waddstr(stdscr, res);
  waddstr(stdscr, res2);
  sprintf(def, "%llu", val_cur);
  if (get_string(response, LINE_LENGTH, def) > 0)
  {
#ifdef HAVE_ATOLL
    tmp_val = atoll(response);
#else
    tmp_val = atol(response);
#endif
    if (val_min==val_max || (tmp_val >= val_min && tmp_val <= val_max))
      return tmp_val;
  }
  return val_cur;
}

void dump_ncurses(const void *nom_dump, unsigned int lng)
{
  WINDOW *window=newwin(0,0,0,0);	/* full screen */
  keypad(window, TRUE); /* Need it to get arrow key */
  aff_copy(window);
  dump(window, nom_dump, lng);
  delwin(window);
  (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
  touchwin(stdscr);
#endif
}

void dump(WINDOW *window, const void *nom_dump,unsigned int lng)
{
  unsigned int i,j;
  unsigned int nbr_line;
  unsigned char car;
  unsigned int pos=0;
  int done=0;
  unsigned int menu=2;   /* default : quit */
  const char *options="PNQ";
  struct MenuItem menuDump[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q',"Quit","Quit dump section"},
    { 0, NULL, NULL }
  };
  dump_log(nom_dump,lng);
  nbr_line=(lng+0x10-1)/0x10;
  if(nbr_line<=DUMP_MAX_LINES)
  {
    options="Q";
  }
  /* ncurses interface */
  mvwaddstr(window,DUMP_Y,DUMP_X,msg_DUMP_HEXA);
  /* On pourrait utiliser wscrl */
  do
  {
    for (i=pos; (i<nbr_line)&&((i-pos)<DUMP_MAX_LINES); i++)
    {
      wmove(window,DUMP_Y+i-pos,DUMP_X);
      wclrtoeol(window);
      wdoprintf(window,"%04X ",i*0x10);
      for(j=0; j< 0x10;j++)
      {
	if(i*0x10+j<lng)
	{
	  car=*((const unsigned char*)nom_dump+i*0x10+j);
	  wdoprintf(window,"%02x", car);
	}
	else
	  wdoprintf(window,"  ");
	if(j%4==(4-1))
	  wdoprintf(window," ");
      }
      wdoprintf(window,"  ");
      for(j=0; j< 0x10;j++)
      {
	if(i*0x10+j<lng)
	{
	  car=*((const unsigned char*)nom_dump+i*0x10+j);
	  if ((car<32)||(car >= 127))
	    wdoprintf(window,".");
	  else
	    wdoprintf(window,"%c",  car);
	}
	else
	  wdoprintf(window," ");
      }
    }
    switch (wmenuSelect(window,INTER_DUMP_Y, INTER_DUMP_X, menuDump, 8, options, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, menu))
    {
      case 'p':
      case 'P':
      case KEY_UP:
	if(strchr(options,'N')!=NULL)
	{
	  menu=0;
	  if(pos>0)
	    pos--;
	}
	break;
      case 'n':
      case 'N':
      case KEY_DOWN:
	if(strchr(options,'N')!=NULL)
	{
	  menu=1;
	  if(pos<nbr_line-DUMP_MAX_LINES)
	    pos++;
	}
	break;
      case KEY_PPAGE:
	if(strchr(options,'N')!=NULL)
	{
	  menu=0;
	  if(pos>DUMP_MAX_LINES-1)
	    pos-=DUMP_MAX_LINES-1;
	  else
	    pos=0;
	}
	break;
      case KEY_NPAGE:
	if(strchr(options,'N')!=NULL)
	{
	  menu=1;
	  if(pos<nbr_line-DUMP_MAX_LINES-(DUMP_MAX_LINES-1))
	    pos+=DUMP_MAX_LINES-1;
	  else
	    pos=nbr_line-DUMP_MAX_LINES;
	}
	break;
      case key_ESC:
      case 'q':
      case 'Q':
	done = TRUE;
	break;
    }
  } while(done==FALSE);
}

void dump2(WINDOW *window, const void *dump_1, const void *dump_2, const unsigned int lng)
{
  unsigned int i,j;
  unsigned int nbr_line;
  unsigned int pos=0;
  int done=0;
  unsigned int menu=2;   /* default : quit */
  const char *options="PNQ";
  struct MenuItem menuDump[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q',"Quit","Quit dump section"},
    { 0, NULL, NULL }
  };
  dump2_log(dump_1, dump_2, lng);
  /* ncurses interface */
  nbr_line=(lng+0x08-1)/0x08;
  if(nbr_line<=DUMP_MAX_LINES)
  {
    options="Q";
  }
  do
  {
    for (i=pos; (i<nbr_line)&&((i-pos)<DUMP_MAX_LINES); i++)
    {
      wmove(window,DUMP_Y+i-pos,DUMP_X);
      wclrtoeol(window);
      wdoprintf(window,"%04X ",i*0x08);
      for(j=0; j<0x08;j++)
      {
	if(i*0x08+j<lng)
	{
	  unsigned char car1=*((const unsigned char*)dump_1+i*0x08+j);
	  unsigned char car2=*((const unsigned char*)dump_2+i*0x08+j);
	  if(car1!=car2)
	    wattrset(window, A_REVERSE);
	  wdoprintf(window,"%02x", car1);
	  if(car1!=car2)
	    wattroff(window, A_REVERSE);
	}
	else
	  wdoprintf(window," ");
	if(j%4==(4-1))
	  wdoprintf(window," ");
      }
      wdoprintf(window,"  ");
      for(j=0; j<0x08;j++)
      {
	if(i*0x08+j<lng)
	{
	  unsigned char car1=*((const unsigned char*)dump_1+i*0x08+j);
	  unsigned char car2=*((const unsigned char*)dump_2+i*0x08+j);
	  if(car1!=car2)
	    wattrset(window, A_REVERSE);
	  if ((car1<32)||(car1 >= 127))
	    wdoprintf(window,".");
	  else
	    wdoprintf(window,"%c",  car1);
	  if(car1!=car2)
	    wattroff(window, A_REVERSE);
	}
	else
	  wdoprintf(window," ");
      }
      wdoprintf(window,"  ");
      for(j=0; j<0x08;j++)
      {
	if(i*0x08+j<lng)
	{
	  unsigned char car1=*((const unsigned char*)dump_1+i*0x08+j);
	  unsigned char car2=*((const unsigned char*)dump_2+i*0x08+j);
	  if(car1!=car2)
	    wattrset(window, A_REVERSE);
	  wdoprintf(window,"%02x", car2);
	  if(car1!=car2)
	    wattroff(window, A_REVERSE);
	  if(j%4==(4-1))
	    wdoprintf(window," ");
	}
	else
	  wdoprintf(window," ");
      }
      wdoprintf(window,"  ");
      for(j=0; j<0x08;j++)
      {
	if(i*0x08+j<lng)
	{
	  unsigned char car1=*((const unsigned char*)dump_1+i*0x08+j);
	  unsigned char car2=*((const unsigned char*)dump_2+i*0x08+j);
	  if(car1!=car2)
	    wattrset(window, A_REVERSE);
	  if ((car2<32)||(car2 >= 127))
	    wdoprintf(window,".");
	  else
	    wdoprintf(window,"%c",  car2);
	  if(car1!=car2)
	    wattroff(window, A_REVERSE);
	}
	else
	  wdoprintf(window," ");
      }
    }
    switch (wmenuSelect(window,INTER_DUMP_Y, INTER_DUMP_X, menuDump, 8, options, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, menu))
    {
      case 'p':
      case 'P':
      case KEY_UP:
	if(strchr(options,'N')!=NULL)
	{
	  menu=0;
	  if(pos>0)
	    pos--;
	}
	break;
      case 'n':
      case 'N':
      case KEY_DOWN:
	if(strchr(options,'N')!=NULL)
	{
	  menu=1;
	  if(pos<nbr_line-DUMP_MAX_LINES)
	    pos++;
	}
	break;
      case KEY_PPAGE:
	if(strchr(options,'N')!=NULL)
	{
	  menu=0;
	  if(pos>DUMP_MAX_LINES-1)
	    pos-=DUMP_MAX_LINES-1;
	  else
	    pos=0;
	}
	break;
      case KEY_NPAGE:
	if(strchr(options,'N')!=NULL)
	{
	  menu=1;
	  if(pos<nbr_line-DUMP_MAX_LINES-(DUMP_MAX_LINES-1))
	    pos+=DUMP_MAX_LINES-1;
	  else
	    pos=nbr_line-DUMP_MAX_LINES;
	}
	break;
      case key_ESC:
      case 'q':
      case 'Q':
	done = TRUE;
	break;
    }
  } while(done==FALSE);
}

static char intr_buffer_screen[MAX_LINES][LINE_LENGTH+1];
static int intr_nbr_line=0;

int aff_buffer(const buffer_cmd_t cmd, const char *_format, ...)
{
  switch(cmd)
  {
    case BUFFER_RESET:
      {
	int i;
	intr_nbr_line=0;
	for(i=0;i<MAX_LINES;i++)
	  memset(intr_buffer_screen[i],0,LINE_LENGTH+1);
      }
      break;
    case BUFFER_ADD:
      {
	char tmp_line[BUFFER_LINE_LENGTH+1];
	char *pos_in_tmp_line=tmp_line;
	va_list ap;
	va_start(ap,_format);
	memset(tmp_line,'\0',sizeof(tmp_line));
	vsnprintf(tmp_line,BUFFER_LINE_LENGTH,_format,ap);
	va_end(ap);
	while(pos_in_tmp_line!=NULL && (intr_nbr_line<MAX_LINES))
	{
	  char *ret_ligne= strchr(pos_in_tmp_line,'\n');
	  if(ret_ligne!=NULL)
	  {
	    strncat(intr_buffer_screen[intr_nbr_line],pos_in_tmp_line,
		ret_ligne-pos_in_tmp_line<LINE_LENGTH-strlen(intr_buffer_screen[intr_nbr_line])?ret_ligne-pos_in_tmp_line:LINE_LENGTH-strlen(intr_buffer_screen[intr_nbr_line]));
/*	    if(strlen(intr_buffer_screen[intr_nbr_line])>0) */
	    intr_nbr_line++;
	    ret_ligne++;
	  }
	  else
	  {
	    strncat(intr_buffer_screen[intr_nbr_line],pos_in_tmp_line,LINE_LENGTH-strlen(intr_buffer_screen[intr_nbr_line]));
	  }
	  pos_in_tmp_line=ret_ligne;
	}
	/*	log_trace("aff_intr_buffer_screen %d =>%s<=\n",intr_nbr_line,tmp_line); */
	if(intr_nbr_line>=MAX_LINES)
	{
	  log_warning("aff_intr_buffer_screen too much lines =>%s<=\n",tmp_line);
	}
      }
      break;
    case BUFFER_SHOW:
      {
	int i;
	int pos=intr_nbr_line-DUMP_MAX_LINES<0?0:intr_nbr_line-DUMP_MAX_LINES;
	if(intr_buffer_screen[intr_nbr_line][0]!='\0')
	  intr_nbr_line++;
	/* curses interface */
	for (i=pos; (i<intr_nbr_line)&&((i-pos)<DUMP_MAX_LINES); i++)
	{
	  wmove(stdscr,DUMP_Y+1+i-pos,DUMP_X);
	  wclrtoeol(stdscr);
	  wdoprintf(stdscr,"%s",intr_buffer_screen[i]);
	}
	wrefresh(stdscr);
      }
      break;
    case BUFFER_WRITE:
      {
	int i;
	if(intr_buffer_screen[intr_nbr_line][0]!='\0')
	  intr_nbr_line++;
	/* to log file and stdout */
	for(i=0;i<intr_nbr_line;i++)
	{
	  printf("%s\n",intr_buffer_screen[i]);
	  log_info("%s\n",intr_buffer_screen[i]);
	}
      }
      break;
  }
  return 0;
}

void screen_buffer_to_log()
{
  int i;
  if(intr_buffer_screen[intr_nbr_line][0]!='\0')
    intr_nbr_line++;
  /* to log file */
  for(i=0;i<intr_nbr_line;i++)
    log_info("%s\n",intr_buffer_screen[i]);
}

int screen_buffer_display(WINDOW *window, const char *options_org, const struct MenuItem *menuItems)
{
  unsigned int menu=0;
  return screen_buffer_display_ext(window,options_org,menuItems,&menu);
}

int screen_buffer_display_ext(WINDOW *window, const char *options_org, const struct MenuItem *menuItems, unsigned int *menu)
{
  int i;
  int first_line_to_display=0;
  int current_line=0;
  int done=0;
  char options[20];
  struct MenuItem menuDefault[]=
  {
    { 'P', "Previous",""},
    { 'N', "Next","" },
    { 'Q', "Quit","Quit this section"},
    { 0, NULL, NULL }
  };
  strncpy(options,"Q",sizeof(options));
  strncat(options,options_org,sizeof(options)-strlen(options));
  if(intr_buffer_screen[intr_nbr_line][0]!='\0')
    intr_nbr_line++;
  /* to log file */
  for(i=0;i<intr_nbr_line;i++)
    log_info("%s\n",intr_buffer_screen[i]);
  /* curses interface */
  do
  {
    int key;
    wmove(window, INTER_ANALYSE_Y-1, INTER_ANALYSE_X+4);
    wclrtoeol(window);
    if(first_line_to_display>0)
      wdoprintf(window, "Previous");
    for (i=first_line_to_display; (i<intr_nbr_line)&&((i-first_line_to_display)<INTER_MAX_LINES); i++)
    {
      wmove(window,INTER_ANALYSE_Y+i-first_line_to_display,INTER_ANALYSE_X);
      wclrtoeol(window);
      if(i==current_line && intr_nbr_line>INTER_MAX_LINES && has_colors())
	wattrset(window, A_REVERSE);
      wdoprintf(window,"%s",intr_buffer_screen[i]);
      if(i==current_line && intr_nbr_line>INTER_MAX_LINES && has_colors())
	wattroff(window, A_REVERSE);
    }
    wmove(window, INTER_ANALYSE_Y+INTER_MAX_LINES, INTER_ANALYSE_X+4);
    wclrtoeol(window);
    if(i<intr_nbr_line)
	wdoprintf(window, "Next");
    key=wmenuSelect_ext(window,INTER_ANALYSE_MENU_Y, INTER_ANALYSE_MENU_X, (menuItems!=NULL?menuItems:menuDefault), 8, options, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, menu,NULL);
    switch (key)
    {
      case key_ESC:
      case 'q':
      case 'Q':
	done = TRUE;
	break;
      case 'p':
      case 'P':
      case KEY_UP:
	if(current_line>0)
	  current_line--;
	if(current_line<first_line_to_display)
	  first_line_to_display=current_line;
	break;
      case 'n':
      case 'N':
      case KEY_DOWN:
        if(current_line<intr_nbr_line-1)
	  current_line++;
	if(current_line>=first_line_to_display+INTER_MAX_LINES)
	  first_line_to_display=current_line-INTER_MAX_LINES+1;
	break;
      case KEY_PPAGE:
	if(current_line>INTER_MAX_LINES-1)
	  current_line-=INTER_MAX_LINES-1;
	else
	  current_line=0;
	if(current_line<first_line_to_display)
	  first_line_to_display=current_line;
	break;
      case KEY_NPAGE:
	if(current_line+INTER_MAX_LINES-1 < intr_nbr_line-1)
	  current_line+=INTER_MAX_LINES-1;
	else
	  current_line=intr_nbr_line-1;
	if(current_line>=first_line_to_display+INTER_MAX_LINES)
	  first_line_to_display=current_line-INTER_MAX_LINES+1;
	break;
      default:
	if(strchr(options,toupper(key))!=NULL)
	  return toupper(key);
	break;
    }
  } while(done!=TRUE);
  return 0;
}

void aff_CHS(const CHS_t * CHS)
{
  wdoprintf(stdscr,"%5u %3u %2u ", CHS->cylinder, CHS->head, CHS->sector);
}

void aff_CHS_buffer(const CHS_t * CHS)
{
  aff_buffer(BUFFER_ADD,"%5u %3u %2u ", CHS->cylinder, CHS->head, CHS->sector);
}

const char *aff_part_aux(const aff_part_type_t newline, const disk_t *disk_car, const partition_t *partition)
{
  char status=' ';
  static char msg[200];
  unsigned int pos=0;
  const arch_fnct_t *arch=(partition->arch!=NULL?partition->arch:disk_car->arch);
  msg[sizeof(msg)-1]=0;
  switch(newline)
  {
    case AFF_PART_ORDER:
      if((partition->status!=STATUS_EXT_IN_EXT) && (partition->order!=NO_ORDER))
	pos+=snprintf(&msg[pos],sizeof(msg)-pos-1,"%2d ", partition->order);
      else
	pos+=snprintf(&msg[pos],sizeof(msg)-pos-1,"   ");
      break;
    case AFF_PART_NONL:
    case AFF_PART_SHORT:
      break;
  }
  if(newline!=AFF_PART_SHORT)
  {
    switch(partition->status)
    {
      case STATUS_PRIM:           status='P'; break;
      case STATUS_PRIM_BOOT:      status='*'; break;
      case STATUS_EXT:            status='E'; break;
      case STATUS_EXT_IN_EXT:     status='X'; break;
      case STATUS_LOG:            status='L'; break;
      case STATUS_DELETED:        status='D'; break;
      default:			  status=' '; break;
    }
  }
  pos+=snprintf(&msg[pos],sizeof(msg)-pos-1,"%c", status);
  if(arch->get_partition_name(partition)!=NULL)
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, " %-20s ",
	arch->get_partition_name(partition));
  else
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, " Sys=%02X               ", arch->get_part_type(partition));
  if(disk_car->arch==&arch_mac || (disk_car->CHS.head==0 && disk_car->CHS.sector==1))
  {
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, " %10lu ", (long unsigned)(partition->part_offset/disk_car->sector_size));
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, "%10lu ", (long unsigned)((partition->part_offset+partition->part_size-1)/disk_car->sector_size));
  }
  else
  {
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1,"%5u %3u %2u ",
	offset2cylinder(disk_car,partition->part_offset),
	offset2head(    disk_car,partition->part_offset),
	offset2sector(  disk_car,partition->part_offset));
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, "%5u %3u %2u ",
	offset2cylinder(disk_car,partition->part_offset+partition->part_size-1),
	offset2head(    disk_car,partition->part_offset+partition->part_size-1),
	offset2sector(  disk_car,partition->part_offset+partition->part_size-1));
  }
  pos+=snprintf(&msg[pos],sizeof(msg)-pos-1,"%10lu", (long unsigned)(partition->part_size/disk_car->sector_size));
  if(partition->name[0]!='\0')
    pos+=snprintf(&msg[pos],sizeof(msg)-pos-1, " [%s]",partition->name);
  return msg;
}

void aff_part(WINDOW *window,const aff_part_type_t newline,const disk_t *disk_car,const partition_t *partition)
{
  const char *msg;
  msg=aff_part_aux(newline, disk_car, partition);
  wdoprintf(window,"%s",msg);
}

void aff_part_buffer(const aff_part_type_t newline,const disk_t *disk_car,const partition_t *partition)
{
  const char *msg;
  msg=aff_part_aux(newline, disk_car, partition);
  aff_buffer(BUFFER_ADD,"%s\n", msg);
}

void aff_LBA2CHS(const disk_t *disk_car, const unsigned long int pos_LBA)
{
  unsigned long int tmp;
  unsigned long int cylinder, head, sector;
  tmp=disk_car->CHS.sector;
  sector=(pos_LBA%tmp)+1;
  tmp=pos_LBA/tmp;
  cylinder=tmp/(disk_car->CHS.head+1);
  head=tmp%(disk_car->CHS.head+1);
  wdoprintf(stdscr,"%lu/%lu/%lu", cylinder,head,sector);
}

void log_CHS_from_LBA(const disk_t *disk_car, const unsigned long int pos_LBA)
{
  unsigned long int tmp;
  unsigned long int cylinder, head, sector;
  tmp=disk_car->CHS.sector;
  sector=(pos_LBA%tmp)+1;
  tmp=pos_LBA/tmp;
  cylinder=tmp/(disk_car->CHS.head+1);
  head=tmp%(disk_car->CHS.head+1);
  log_info("%lu/%lu/%lu", cylinder,head,sector);
}

int ask_YN(WINDOW *window)
{
  char res;
  curs_set(1);
  wrefresh(window);
  do
  {
    res=toupper(wgetch(window));
  } while((res!=c_NO)&&(res!=c_YES));
  curs_set(0);
  wdoprintf(window,"%c\n",res);
  return (res==c_YES);
}

int ask_confirmation(const char*_format, ...)
{
  va_list ap;
  int res;
  WINDOW *window=newwin(0,0,0,0);	/* full screen */
  aff_copy(window);
  va_start(ap,_format);
  vaff_txt(4, window, _format, ap);
  va_end(ap);
  res=ask_YN(window);
  delwin(window);
  (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
  touchwin(stdscr);
#endif
  return res;
}

int wdoprintf(WINDOW *window, const char *_format, ...)
{
  char res[800];
  va_list ap;
  va_start(ap,_format);
  vsnprintf(res,sizeof(res),_format,ap);
  va_end(ap);
  res[sizeof(res)-1]='\0';
#ifdef __MINGW32__
  {
    int len=strlen(res);
    if(res[len-1]=='\n' && len<sizeof(res)-1)
    {
      res[len]='\r';
      res[len+1]=0;
    }
  }
#endif
  return waddstr(window,res);
}

int display_message(const char*msg)
{
  int pipo=0;
  static struct MenuItem menuGeometry[]=
  {
    { 'Q', "Ok", "" },
    { 0, NULL, NULL }
  };
  WINDOW *window=newwin(0,0,0,0);	/* full screen */
  aff_copy(window);
  mvwaddstr(window,5,0,msg);
  wmenuSimple(window,menuGeometry, pipo);
  delwin(window);
  (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
  touchwin(stdscr);
#endif
  log_info("%s",msg);
  return 0;
}

void not_implemented(const char *msg)
{
  WINDOW *window=newwin(0,0,0,0);	/* full screen */
  aff_copy(window);
  wmove(window,7,0);
  wdoprintf(window,"Function %s not implemented",msg);
  log_warning("Function %s not implemented\n",msg);
  wmove(window,22,0);
  wattrset(window, A_REVERSE);
  wdoprintf(window,"[ Abort ]");
  wattroff(window, A_REVERSE);
  wrefresh(window);
  while(wgetch(window)==ERR);
  delwin(window);
  (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
  touchwin(stdscr);
#endif
}

int start_ncurses(const char *prog_name, const char *real_prog_name)
{
  /* use_env(TRUE); */
  {
    int term_overwrite;
    SCREEN *sp=NULL;
    for(term_overwrite=0;sp==NULL && term_overwrite<=1;term_overwrite++)
    {
#ifdef HAVE_SETENV
#ifdef DJGPP
#elif defined(TARGET_BSD)
      setenv("TERM","cons25",term_overwrite);
#elif defined(TARGET_LINUX)
      setenv("TERM","linux",term_overwrite);
#elif defined(__CYGWIN__)
      setenv("TERM","cygwin",term_overwrite);
#elif defined(__OS2__)
      setenv("TERM","ansi",term_overwrite);
#endif
#endif
#if defined(DJGPP) || defined(__MINGW32__)
	sp=initscr();
#else
      {
	sp=newterm(NULL,stdout,stdin);
#ifdef HAVE_SETENV
#ifdef HAVE_DIRNAME
	if(sp==NULL)
	{
	  char buf[2048];
#ifdef HAVE_READLINK
	  {
	    int len;
	    len=readlink(real_prog_name,buf,sizeof(buf)-1);
	    if(len>=0)
	    {
	      buf[len]='\0';
	    }
	    else
	    {
	      strncpy(buf,real_prog_name,sizeof(buf)-1);
	      buf[sizeof(buf)-1]='\0';
	    }
	  }
#else	// HAVE_READLINK
	  {
	    strncpy(buf,real_prog_name,sizeof(buf)-1);
	    buf[sizeof(buf)-1]='\0';
	  }
#endif	// HAVE_READLINK
	  setenv("TERMINFO",dirname(buf),1);
	  sp=newterm(NULL,stdout,stdin);
	}
#endif	// HAVE_DIRNAME
	if(sp==NULL)
	{
	  setenv("TERMINFO",".",1);
	  sp=newterm(NULL,stdout,stdin);
	}
#endif
      }
#endif
    }
    if(sp==NULL)
    {
#if defined(DJGPP) || defined(__MINGW32__)
      log_critical("initscr() has failed. Exiting\n");
      printf("initscr() has failed. Exiting\n");
#elif defined(__CYGWIN__)
      log_critical("Terminfo file is missing.\n");
      printf("Terminfo file c\\cygwin is missing.\n");
      printf("Extract all files and subdirectories before running the program.\n");
#else
      log_critical("Terminfo file is missing.\n");
      printf("Terminfo file is missing.\n");
      printf("Extract all files and subdirectories before running the program.\n");
#endif
      printf("Press Enter key to quit.\n");
      getchar();
      return 1;
    }
  }
  noecho();
#ifndef DJGPP
  nonl(); /*don't use for Dos version but enter will work with it... dilema */
#endif
  /*  intrflush(stdscr, FALSE); */
  cbreak();
  /* Should solve a problem with users who redefined the colors */
  if(has_colors())
  {
    start_color();
    init_pair(1, COLOR_RED, COLOR_BLACK);
    init_pair(2, COLOR_GREEN, COLOR_BLACK);
  }
  curs_set(0);
  {
    int quit=0;
    while(LINES<25 && quit==0)
    {
      if(LINES>=8)
      {
	aff_copy(stdscr);
	wmove(stdscr,4,0);
	wdoprintf(stdscr,"%s need 25 lines to work.", prog_name);
	wmove(stdscr,5,0);
	wdoprintf(stdscr,"Please enlarge the terminal.");
	wmove(stdscr,LINES-2,0);
	wattrset(stdscr, A_REVERSE);
	wdoprintf(stdscr,"[ Quit ]");
	wattroff(stdscr, A_REVERSE);
	wrefresh(stdscr);
	switch(wgetch(stdscr))
	{
	  case KEY_ENTER:
#ifdef PADENTER
	  case PADENTER:
#endif
	  case '\n':
	  case '\r':
	    quit=1;
	    break;
	}
      }
      else
	quit=1;
    }
  }
  if(LINES<25)
  {
    end_ncurses();
    printf("%s need 25 lines to work.\nPlease enlarge the terminal and restart %s.\n",prog_name,prog_name);
    log_critical("Terminal has only %u lines\n",LINES);
    return 1;
  }
  return 0;
}

int end_ncurses()
{
  wclear(stdscr);
  wrefresh(stdscr);
  nl();
  endwin();
  return 0;
}

char *ask_log_location(const char*filename)
{
  static char response[LINE_LENGTH];
  aff_copy(stdscr);
  wmove(stdscr,6,0);
  wdoprintf(stdscr,"Cannot open %s: %s\n",filename, strerror(errno));
  wmove(stdscr,8,0);
  wdoprintf(stdscr,"Please enter the full log filename or press ");
  if(has_colors())
    wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
  wdoprintf(stdscr,"Enter");
  if(has_colors())
    wbkgdset(stdscr,' ' | COLOR_PAIR(0));
  wmove(stdscr,9,0);
  wdoprintf(stdscr,"to abort log file creation.\n");
  if (get_string(response, LINE_LENGTH, NULL) > 0)
    return response;
  return NULL;
}

/*
  0: no log
  1: append
  2: create
*/
int ask_log_creation()
{
  int command;
  unsigned int menu=0;
  static struct MenuItem menuLogCreation[]=
  {
	{'C',"Create","Create a new log file"},
	{'A',"Append","Append information to log file"},
	{'Q',"No Log","Don't record anything"},
	{0,NULL,NULL}
  };
  aff_copy(stdscr);
  wmove(stdscr,5,0);
  wdoprintf(stdscr,"TestDisk is a data recovery designed to help recover lost partitions");
  wmove(stdscr,6,0);
  wdoprintf(stdscr,"and/or make non-booting disks bootable again when these symptoms");
  wmove(stdscr,7,0);
  wdoprintf(stdscr,"are caused by faulty software, certain types of viruses or human error.");
  wmove(stdscr,8,0);
  wdoprintf(stdscr,"It can also be used to repair some filesystem errors.");
  wmove(stdscr,10,0);
  wdoprintf(stdscr,"Information gathered during TestDisk use can be recorded for later");
  wmove(stdscr,11,0);
  wdoprintf(stdscr,"review. If you choose to create the text file, ");
  if(has_colors())
    wbkgdset(stdscr,' ' | A_BOLD | COLOR_PAIR(0));
  wdoprintf(stdscr,"testdisk.log");
  if(has_colors())
    wbkgdset(stdscr,' ' | COLOR_PAIR(0));
  wdoprintf(stdscr," , it");
  wmove(stdscr,12,0);
  wdoprintf(stdscr,"will contain TestDisk options, technical information and various");
  wmove(stdscr,13,0);
  wdoprintf(stdscr,"outputs; including any folder/file names TestDisk was used to find and");
  wmove(stdscr,14,0);
  wdoprintf(stdscr,"list onscreen.");
  wmove(stdscr,16,0);
  wdoprintf(stdscr,"Use arrow keys to select, then press Enter key:");
  while(1)
  {
    command = wmenuSelect_ext(stdscr,17, 0, menuLogCreation, 8,
	"CAQ", MENU_VERT | MENU_VERT_WARN | MENU_BUTTON, &menu,NULL);
    switch(command)
    {
      case 'C':
      case 'c':
	return 2;
      case 'A':
      case 'a':
	return 1;
      case 'Q':
      case 'q':
	return 0;
      default:
	break;
    }
  }
}

void intrf_no_disk(const char *prog_name)
{
  aff_copy(stdscr);
  wmove(stdscr,4,0);
  wdoprintf(stdscr,"  %s is free software, and",prog_name);
  wmove(stdscr,5,0);
  wdoprintf(stdscr,"comes with ABSOLUTELY NO WARRANTY.");
  wmove(stdscr,7,0);
  wdoprintf(stdscr,"No harddisk found\n");
  wmove(stdscr,8,0);
#if defined(__CYGWIN__) || defined(__MINGW32__)
  wdoprintf(stdscr,"You need to be administrator to use %s.\n", prog_name);
  wmove(stdscr,9,0);
  wdoprintf(stdscr,"Under Win9x, use the DOS version instead.\n");
  wmove(stdscr,10,0);
  wdoprintf(stdscr,"Under Vista, select %s, right-click and choose \"Run as administrator\".\n", prog_name);
  log_critical("You need to be administrator to use %s.\n", prog_name);
#else
#ifndef DJGPP
#ifdef HAVE_GETEUID
  if(geteuid()!=0)
  {
    wdoprintf(stdscr,"You need to be root to use %s.\n", prog_name);
    log_critical("You need to be root to use %s.\n", prog_name);
#if defined(__APPLE__)
    wmove(stdscr,9,0);
    wdoprintf(stdscr,"Use the sudo command to launch %s.\n", prog_name);
#endif
  }
#endif
#endif
#endif
  wmove(stdscr,22,0);
  wattrset(stdscr, A_REVERSE);
  wdoprintf(stdscr,"[ Quit ]");
  wattroff(stdscr, A_REVERSE);
  wrefresh(stdscr);
  while(wgetch(stdscr)==ERR);
}

int check_enter_or_s(WINDOW *window)
{
  switch(wgetch_nodelay(window))
  {
    case KEY_ENTER:
#ifdef PADENTER
    case PADENTER:
#endif
    case '\n':
    case '\r':
    case 's':
    case 'S':
      return 1;
      break;
  }
  return 0;
}

int interface_partition_type(disk_t *disk_car, const int allow_partial_last_cylinder, const int debug, char**current_cmd)
{
  const arch_fnct_t *arch_list[]={&arch_i386, &arch_mac, &arch_none, &arch_sun, &arch_mac, NULL};
  if(*current_cmd!=NULL)
  {
    int keep_asking;
    do
    {
      int i;
      keep_asking=0;
      while(*current_cmd[0]==',')
	(*current_cmd)++;
      for(i=0;arch_list[i]!=NULL;i++)
	if(strncmp(*current_cmd, arch_list[i]->part_name_option, strlen(arch_list[i]->part_name_option))==0)
	{
	  (*current_cmd)+=strlen(arch_list[i]->part_name_option);
	  disk_car->arch=arch_list[i];
	  keep_asking=1;
	}
    } while(keep_asking>0);
  }
  else
  {
    unsigned int menu;
    for(menu=0;arch_list[menu]!=NULL && disk_car->arch!=arch_list[menu];menu++);
    if(arch_list[menu]==NULL)
    {
      menu=0;
      disk_car->arch=arch_list[menu];
    }
    /* ncurses interface */
    {
      int car;
      int real_key;
      struct MenuItem menuOptions[]=
      {
	{ 'I', arch_i386.part_name, "Intel/PC partition" },
	{ 'M', arch_mac.part_name, "Apple partition map" },
	{ 'N', arch_none.part_name, "Non partitioned media" },
	{ 'S', arch_sun.part_name, "Sun Solaris partition"},
	{ 'X', arch_xbox.part_name, "XBox partition"},
	{ 'Q', "Return", "Return to disk selection"},
	{ 0, NULL, NULL }
      };
      aff_copy(stdscr);
      wmove(stdscr,5,0);
      wdoprintf(stdscr,"%s\n",disk_car->description_short(disk_car));
      wmove(stdscr,INTER_PARTITION_Y-1,0);
      wdoprintf(stdscr,"Please select the partition table type, press Enter when done.");
      wmove(stdscr,20,0);
      wdoprintf(stdscr,"Note: Do NOT select 'None' for media with only a single partition. It's very");
      wmove(stdscr,21,0);
      wdoprintf(stdscr,"rare for a drive to be 'Non-partitioned'.");
      car=wmenuSelect_ext(stdscr,INTER_PARTITION_Y, INTER_PARTITION_X, menuOptions, 7, "IMNSXQ", MENU_BUTTON | MENU_VERT | MENU_VERT_WARN, &menu,&real_key);
      switch(car)
      {
	case 'i':
	case 'I':
	  disk_car->arch=&arch_i386;
	  break;
	case 'm':
	case 'M':
	  disk_car->arch=&arch_mac;
	  break;
	case 'n':
	case 'N':
	  disk_car->arch=&arch_none;
	  break;
	case 's':
	case 'S':
	  disk_car->arch=&arch_sun;
	  break;
	case 'x':
	case 'X':
	  disk_car->arch=&arch_xbox;
	  break;
	case 'q':
	case 'Q':
	  return 1;
      }
    }
  }
  log_info("%s\n",disk_car->description_short(disk_car));
  log_info("Partition table type: %s\n",disk_car->arch->part_name);
  hd_update_geometry(disk_car,allow_partial_last_cylinder,debug);
  return 0;
}

#define PATH_SEP '/'
#define SPATH_SEP "/"
#if defined(__CYGWIN__)
#define PATH_DRIVE_LENGTH 9
#elif defined(DJGPP) || defined(__OS2__)
#define PATH_DRIVE_LENGTH 3
#endif

static void set_parent_directory(char *dst_directory)
{
  int i;
  int last_sep=-1;
  for(i=0;dst_directory[i]!='\0';i++)
    if(dst_directory[i]==PATH_SEP)
      last_sep=i;
#ifdef __CYGWIN__
  /* /cygdrive */
  if(last_sep>PATH_DRIVE_LENGTH)
    dst_directory[last_sep]='\0';
  else
    dst_directory[PATH_DRIVE_LENGTH]='\0';
#elif defined(DJGPP) || defined(__OS2__)
  if(last_sep>PATH_DRIVE_LENGTH)
    dst_directory[last_sep]='\0';
  else
    dst_directory[0]='\0';
#else
  if(last_sep>1)
    dst_directory[last_sep]='\0';
  else
    dst_directory[1]='\0';
#endif
}

char *ask_location(const char*msg, const char *src_dir)
{
  char dst_directory[4096];
  char *res=NULL;
  int quit;
  WINDOW *window=newwin(0,0,0,0);	/* full screen */
  aff_copy(window);
#ifdef HAVE_GETCWD
  if(getcwd(dst_directory, sizeof(dst_directory))==NULL)
    strncpy(dst_directory,".",sizeof(dst_directory));
#else
  strncpy(dst_directory,".",sizeof(dst_directory));
#endif
  do
  {
    DIR* dir;
    struct list_head *dir_current;
    int offset=0;
    int pos_num=0;
    static struct file_info dir_list = {
      .list = LIST_HEAD_INIT(dir_list.list),
      .name = {0}
    };
    wmove(window,7,0);
    wclrtoeol(window);	/* before addstr for BSD compatibility */
    if(has_colors())
      wbkgdset(window,' ' | A_BOLD | COLOR_PAIR(0));
    waddstr(window,"Directory listing in progress...");
    if(has_colors())
      wbkgdset(window,' ' | COLOR_PAIR(0));
    wrefresh(window);
#if defined(DJGPP) || defined(__OS2__)
    if(dst_directory[0]=='\0')
    {
      int i;
      for(i='a';i<='z';i++)
      {
	struct file_info *new_drive;
	new_drive=(struct file_info*)MALLOC(sizeof(*new_drive));
	new_drive->name[0]=i;
	new_drive->name[1]=':';
	new_drive->name[2]=PATH_SEP;
	new_drive->name[3]='\0';
	new_drive->stat.st_mode=LINUX_S_IFDIR|LINUX_S_IRWXUGO;
	list_add_tail(&new_drive->list,&dir_list.list);
      }
      dir=NULL;
    }
    else
      dir=opendir(dst_directory);
#else
    dir=opendir(dst_directory);
#endif
    if(dir!=NULL)
    {
      struct dirent *dir_entrie;
      struct file_info *dir_info;
      dir_info=(struct file_info*)MALLOC(sizeof(*dir_info));
      do
      {
	char current_file[4096];
	dir_entrie=readdir(dir);
	if(dir_entrie!=NULL
	  && strlen(dst_directory)+1+strlen(dir_info->name)+1<=sizeof(current_file) &&
	  (dir_entrie->d_name[0]!='.' ||
	  dir_entrie->d_name[1]=='\0' ||
	  (dir_entrie->d_name[1]=='.' && dir_entrie->d_name[2]=='\0'))
#ifdef __CYGWIN__
	  && (strlen(dst_directory)>PATH_DRIVE_LENGTH || dir_entrie->d_name[0]!='.')
#endif
	  )
	{
	  strcpy(current_file,dst_directory);
#if defined(DJGPP) || defined(__OS2__)
	  if(current_file[0]!='\0'&&current_file[1]!='\0'&&current_file[2]!='\0'&&current_file[3]!='\0')
#else
	  if(current_file[1]!='\0')
#endif
	    strcat(current_file,SPATH_SEP);
	  strcat(current_file,dir_entrie->d_name);
#ifdef HAVE_LSTAT
	  if(lstat(current_file,&dir_info->stat)==0)
#else
	  if(stat(current_file,&dir_info->stat)==0)
#endif
	  {
	    if(S_ISDIR(dir_info->stat.st_mode))
	    {
	      /* If the C compiler doesn't use posix definition, st_mode need to be fixed */
#if defined(DJGPP) || defined(__OS2__)
	      dir_info->stat.st_mode=LINUX_S_IFDIR|LINUX_S_IRWXUGO;
#elif defined(__CYGWIN__)
	      if(strlen(dst_directory)<=PATH_DRIVE_LENGTH)
	      {
		dir_info->stat.st_mode=LINUX_S_IFDIR|LINUX_S_IRWXUGO;
		dir_info->stat.st_mtime=0;
		dir_info->stat.st_uid=0;
		dir_info->stat.st_gid=0;
	      }
#endif
	      strncpy(dir_info->name,dir_entrie->d_name,sizeof(dir_info->name));
	      list_add_tail(&dir_info->list,&dir_list.list);
	      dir_info=(struct file_info*)MALLOC(sizeof(*dir_info));
	    }
	  }
	}
      } while(dir_entrie!=NULL);
      free(dir_info);
      closedir(dir);
    }
    dir_current=dir_list.list.next;
    if(dir_list.list.next!=&dir_list.list)
    {
      do
      {
	aff_copy(window);
	wmove(window,7,0);
#ifdef PATH_DRIVE_LENGTH
#ifdef __CYGWIN__
	if(strlen(dst_directory)<=PATH_DRIVE_LENGTH)
	  wdoprintf(window,"To select a drive, use the arrow keys.");
	else
	  wdoprintf(window,"To select another directory, use the arrow keys.");
#else
	if(strlen(dst_directory)<PATH_DRIVE_LENGTH)
	  wdoprintf(window,"To select a drive, use the arrow keys.");
	else
	  wdoprintf(window,"To select another directory, use the arrow keys.");
#endif
#else
	wdoprintf(window,"To select another directory, use the arrow keys.");
#endif
	{
	  struct list_head *dir_walker = NULL;
	  int i=0;
	  list_for_each(dir_walker,&dir_list.list)
	  {
	    if(offset<=i)
	    {
	      struct file_info *dir_info;
	      dir_info=list_entry(dir_walker, struct file_info, list);
	      wmove(window,8+i-offset,0);
	      wclrtoeol(window);	/* before addstr for BSD compatibility */
	      if(dir_walker==dir_current)
		wattrset(window, A_REVERSE);
	      dir_aff_entry(window,dir_info);
	      if(dir_walker==dir_current)
		wattroff(window, A_REVERSE);
	    }
	    i++;
	    if(offset+INTER_DIR<=i)
	      break;
	  }
	}
	{
	  int line=4;
	  char beautifull_dst_directory[4096];
	  if(strcmp(dst_directory,".")==0)
	  {
	    strncpy(beautifull_dst_directory,"the program is running from",sizeof(beautifull_dst_directory));
	    line=aff_txt(line,window,msg,src_dir,beautifull_dst_directory);
	  }
	  else
	  {
#ifdef __CYGWIN__
	    if(strlen(dst_directory)>PATH_DRIVE_LENGTH)
	    {
	      cygwin_conv_to_win32_path(dst_directory,beautifull_dst_directory);
	      line=aff_txt(line,window,msg,src_dir,beautifull_dst_directory);
	    }
#else
#ifdef PATH_DRIVE_LENGTH
	    if(strlen(dst_directory)>=PATH_DRIVE_LENGTH)
#endif
	    {
	      strncpy(beautifull_dst_directory,dst_directory,sizeof(beautifull_dst_directory));
	      line=aff_txt(line,window,msg,src_dir,beautifull_dst_directory);
	    }
#endif
	  }
	}
	wclrtoeol(window);	/* before addstr for BSD compatibility */
	wrefresh(window);
	do
	{
	  quit=0;
	  switch(wgetch(window))
	  {
	    case 'y':
	    case 'Y':
#if defined __CYGWIN__
	      if(strlen(dst_directory)>=PATH_DRIVE_LENGTH)
#endif
	      {
		res=strdup(dst_directory);
		quit=3;
	      }
	      break;
	    case 'n':
	    case 'N':
	      res=NULL;
	      quit=3;
	      break;
	    case KEY_UP:
	      if(dir_current->prev!=&dir_list.list)
	      {
		dir_current=dir_current->prev;
		pos_num--;
		if(pos_num<offset)
		  offset--;
		quit=1;
	      }
	      break;
	    case KEY_DOWN:
	      if(dir_current->next!=&dir_list.list)
	      {
		dir_current=dir_current->next;
		pos_num++;
		if(pos_num>=offset+INTER_DIR)
		  offset++;
		quit=1;
	      }
	      break;
	    case KEY_PPAGE:
	      {
		int i;
		for(i=0;(i<INTER_DIR-1)&&(dir_current->prev!=&dir_list.list);i++)
		{
		  dir_current=dir_current->prev;
		  pos_num--;
		  if(pos_num<offset)
		    offset--;
		  quit=1;
		}
	      }
	      break;
	    case KEY_NPAGE:
	      {
		int i;
		for(i=0;(i<INTER_DIR-1)&&(dir_current->next!=&dir_list.list);i++)
		{
		  dir_current=dir_current->next;
		  pos_num++;
		  if(pos_num>=offset+INTER_DIR)
		    offset++;
		  quit=1;
		}
	      }
	      break;
	    case KEY_RIGHT:
	    case '\r':
	    case '\n':

	    case KEY_ENTER:
#ifdef PADENTER
	    case PADENTER:
#endif
	      if(dir_current!=&dir_list.list)
	      {
		struct file_info *dir_info;
		dir_info=list_entry(dir_current, struct file_info, list);
		if(strcmp(dir_info->name,".")==0)
		{
		}
		else if(strcmp(dir_info->name,"..")==0)
		{
		  set_parent_directory(dst_directory);
		  quit=2;
		}
		else if(strlen(dst_directory)+1+strlen(dir_info->name)+1<=sizeof(dst_directory))
		{
#if defined(DJGPP) || defined(__OS2__)
		  if(dst_directory[0]!='\0'&&dst_directory[1]!='\0'&&dst_directory[2]!='\0'&&dst_directory[3]!='\0')
#else
		    if(dst_directory[1]!='\0')
#endif
		      strcat(dst_directory,SPATH_SEP);
		  strcat(dst_directory,dir_info->name);
		  quit=2;
		}
	      }
	      break;

	  }
	} while(quit==0);
      } while(quit==1);
      {
	struct list_head *dir_walker = NULL;
	struct list_head *dir_walker_next = NULL;
	list_for_each_safe(dir_walker,dir_walker_next,&dir_list.list)
	{
	  struct file_info *dir_info;
	  dir_info=list_entry(dir_walker, struct file_info, list);
	  list_del(dir_walker);
	  free(dir_info);
	}
      }
    }
    else
    {
      set_parent_directory(dst_directory);
      quit=2;
    }
  } while(quit==2);
  delwin(window);
  (void) clearok(stdscr, TRUE);
#ifdef HAVE_TOUCHWIN
  touchwin(stdscr);
#endif
  return res;
}

int vaff_txt(int line, WINDOW *window, const char *_format, va_list ap)
{
  char buffer[1024];
  int i;
  vsnprintf(buffer,sizeof(buffer),_format,ap);
  buffer[sizeof(buffer)-1]='\0';
  for(i=0;buffer[i]!='\0';)
  {
    char buffer2[1024];
    int j,end=i,end2=i;
    for(j=i;buffer[j]!='\0' && (j-i)<COLUMNS;j++)
      if((buffer[j]==' ' || buffer[j]=='\t') && buffer[j+1]!='?' && buffer[j+1]!='[')
      {
	end=j;
	end2=j;
      }
      else if(buffer[j]=='\n')
      {
	end=j;
	end2=j;
	break;
      }
      else if(buffer[j]=='\\' || buffer[j]=='/')
	end2=j;
    if(end2>end && end-i<COLUMNS*3/4)
      end=end2;
    if(end==i)
      end=j-1;
    if(buffer[j]=='\0')
      end=j;
    wmove(window,line,0);
    line++;
    memcpy(buffer2,&buffer[i],end-i+1);
    buffer2[end-i+1]='\0';
    waddstr(window,buffer2);
    for(i=end;buffer[i]==' ' || buffer[i]=='\t' || buffer[i]=='\n'; i++);
  }
  return line;
}

int aff_txt(int line, WINDOW *window, const char *_format, ...)
{
  va_list ap;
  va_start(ap,_format);
  line=vaff_txt(line, window, _format, ap);
  va_end(ap);
  return line;
}
static void dir_aff_entry(WINDOW *window, struct file_info *dir_info)
{
  struct tm		*tm_p;
  char str[11];
  char		datestr[80];
  if(dir_info->stat.st_mtime!=0)
  {
    tm_p = localtime(&dir_info->stat.st_mtime);
    snprintf(datestr, sizeof(datestr),"%2d-%s-%4d %02d:%02d",
	tm_p->tm_mday, monstr[tm_p->tm_mon],
	1900 + tm_p->tm_year, tm_p->tm_hour,
	tm_p->tm_min);
    /* May have to use %d instead of %e */
  } else {
    strncpy(datestr, "                 ",sizeof(datestr));
  }
  mode_string(dir_info->stat.st_mode,str);
  wdoprintf(window, "%s %5u %5u   ", 
      str, (unsigned int)dir_info->stat.st_uid, (unsigned int)dir_info->stat.st_gid);
  wdoprintf(window, "%7llu", (long long unsigned int)dir_info->stat.st_size);
  /* screen may overlap due to long filename */
  wdoprintf(window, " %s %s", datestr, dir_info->name);
}



syntax highlighted by Code2HTML, v. 0.9.1