#include	<stdio.h>
#include	<string.h>
#include	<stdlib.h>
#include	<signal.h>
#include	<errno.h>

#define _NO_USES_DEFINES
#include <wx.h>


#include	"xformat.xpm"

#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>


#define	GETFDPRM	"/usr/bin/getfdprm"
#define SETFDPRM	"/usr/bin/setfdprm"
#define SUPERFORMAT	"/usr/bin/superformat"




#define VERSION	"1.01"




void sigchld_handler(int signo);	// just avoid zombies amd terminate format

#if 0
FILE *mypopen(const char *command, pid_t *pid) // mode gets ignored
{
    const char *filename = tempnam(NULL,NULL);
    pid_t	mypid;
    FILE	*pipe;


//    printf("pipename=%s\n", filename);
//    printf("commandline=%s\n", command);
    if(mknod(filename,0666|S_IFIFO,0) != 0)
	return NULL;

    signal(SIGCHLD,sigchld_handler);
 
    mypid = fork();
    
    if(mypid == 0) // child
    {
	int fd;
	
	fclose(stdout);
	fclose(stderr);
	if((fd=open(filename,O_WRONLY)) != -1)
	{
	    int argc = 0;
	    char *argv[10], *str = (char *)malloc(strlen(command)+1);
	    strcpy(str,command);
	    
	    dup(fd); dup(fd); // make stdout, stderr
	    unlink(filename);

	    argv[0] = strtok(str," \n\r\t");
	    do
	    {
		argv[++argc] = strtok(NULL," \n\r\t");
	    }while(argv[argc] && argc < 10);
       	    
	    execv(argv[0],argv);
	    exit(255);
	}
	exit(1);
    }
    else
    {
	if(pid != NULL)
	    *pid = mypid;
	int fd = open(filename,O_RDONLY);
	pipe = fdopen(fd,"r");
	return pipe;
    }
}
#endif

FILE *mypopen(const char *command, pid_t *pid) // mode gets ignored
{
    pid_t	mypid;
    int		pipe_fds[2];
    
//    printf("commandline=%s\n", command);

     if(pipe(pipe_fds) == -1)
	return NULL;

    signal(SIGCHLD,sigchld_handler);
    mypid = fork();
    
    if(mypid == 0) // child
    {
	close(0); // read end
     	fclose(stdout);
	fclose(stderr);
	int argc = 0;
	char *argv[10], *str = (char *)malloc(strlen(command)+1);
	strcpy(str,command);
	    
	dup(pipe_fds[1]); dup(pipe_fds[1]); // make stdout, stderr
	
	argv[0] = strtok(str," \n\r\t");
	do
	{
	    argv[++argc] = strtok(NULL," \n\r\t");
	}while(argv[argc] && argc < 10);
	
	execv(argv[0],argv);
	exit(255);
    }
    else
    {
	close(pipe_fds[1]); // close write end
	if(pid != NULL)
	    *pid = mypid;
	return fdopen(pipe_fds[0],"r");
    }
}

/*
Groessere Guages.

Filesystem erzeugen.

Errortests! <-- dafuer immer eine ganze Zeile auf einmal  von
superformat lesen und per sscanf() zerlegen.

sigchild_handler nur warten lassen, falls child terminated! (nicht CTRL-Z)
*/

char	*drivenames[] =
{	"fd0","fd1", NULL };

char 	*capacitynames[] =
{
    "360 KB","720 KB","1.2 MB","1.44 MB","1.48 MB","1.49 MB","1.60 MB",
    NULL
};

char	*realcapacitynames[] =
{	"360","720","1200","1440","1476","1494","1600",NULL };

char	*filesystems[] =
{	"msdos", "minix", "ext2", "xiafs", "ext", NULL };

int	tracks_list[] = 
{	40,80,80,80,82,83,80 };

int	sectors_list[] =
{ 9,9,15,18,18,18,20 };

class xfApp : public wxApp
{
    wxIcon	*icon;
public:
    wxFrame	*OnInit(void);
    void	do_mkfs(void);
};



// This statement initialises the whole application
xfApp myapp;

void	abort_button_function(void);

#define FLOPPY_MAJOR 2
#define SECTOR_SIZE 512

class xfFrame : public wxFrame
{
    char buffer[256], name[256], buffer2[256];
    int blocks, tracks;
public:
    int	formatting;
    pid_t	child_pid;
    wxPanel	*panel;
    wxGauge	*verify_gauge, *format_gauge;
    wxChoice	*drive, *capacity, *fs;
    wxCheckBox  *verifyflag, *mkfsflag;
    wxButton	*format_button, *quit_button, *abort_button;
    xfFrame(wxFrame *frame, int x, int y, int w, int h);
    void	ErrorMsg(const char *msg);
    void	format(void);
    void	do_mkfs(void);
    void	abort(void) {}

    Bool	OnClose(void)
    {
	delete this;
	return FALSE;
    }

};



void xfFrame::ErrorMsg(const char *msg)
{
    sprintf(buffer,"ERROR: %s (%s)", msg, strerror(errno));
    SetStatusText(buffer);
}


void	xfFrame::do_mkfs(void)
{
    if(mkfsflag->GetValue())
    {
	int rc;
	
	sprintf(buffer,"executing \"/sbin/mkfs -t %s %s %d\"",
		filesystems[fs->GetSelection()], name, blocks);
	SetStatusText(buffer);
	sprintf(buffer,"/sbin/mkfs -t %s %s %d",
		filesystems[fs->GetSelection()], name, blocks);
	if((rc = system(buffer)) != 0)
	{
	    sprintf(buffer,"Command returned error value %d.", rc);
	    SetStatusText(buffer);
	}
	else
	    SetStatusText("Created filesystem.");
    }    
}


xfFrame 	*xfframe;


void	abort_button_function(void)
{
    if(xfframe->child_pid)
	kill(xfframe->child_pid, SIGINT);
    xfframe->SetStatusText("aborted");    
}

void sigchld_handler(int signo)	// just avoid zombies amd terminate format
{
    int i;
    wait(&i);
    signal(SIGCHLD,sigchld_handler);
}

void	format_button_function(void)
{
    xfframe->format();
}

void	quit_button_function(void)
{
    if(xfframe->child_pid)
	kill(xfframe->child_pid, SIGINT);
    delete xfframe;
}

void	info_button_function(void)
{
    wxMessageBox(
	"XFormat "VERSION" Copyright (C) 1997 by\n"
	"\n"
	"Karsten Ballüder, Lessingstrasse 2,\n"
	"32756 Detmold, Germany\n"
	"(karsten@pimajestix.physik.uni-karlsruhe.de)\n"
	"\n"
	"XFormat has been written using the cross-\n"
	"platform class library wxWindows (Xt library)\n"
	"which is freely available under the GNU Library\n"
	"Public License.\n"
	"\n"
	"GUI:\n"
	"(c) Markus Holzem, Schlossparkstrasse 3,\n"
	"52072 Aachen, Germany\n"
	"Refer to http://www.aiai.ed.ac.uk for more\n"
	"information.\n",
	"XFormat Information",wxOK,xfframe);
}

void
xfFrame::format(void)
{
    char commandline[1024], parameters[100];
    FILE	*pipe;
    int	track, ignore = 0;

    if(formatting)
	return;
    formatting = 1;

    format_button->Enable(FALSE);
    quit_button->Enable(FALSE);
    abort_button->Enable(TRUE);
    
    child_pid = 0;
    
    strcpy(name,"/dev/");
    strcat(name,drivenames[drive->GetSelection()]);
    strcat(name,"h");
    strcat(name,realcapacitynames[capacity->GetSelection()]);
    
    blocks = atoi(realcapacitynames[capacity->GetSelection()]);
    tracks = tracks_list[capacity->GetSelection()];

    format_gauge->SetRange(tracks);
    format_gauge->SetValue(0);
    verify_gauge->SetRange(tracks);
    verify_gauge->SetValue(0);


    sprintf(commandline,GETFDPRM" %s", name);
    
    if(!(pipe = mypopen(commandline,&child_pid)))
    {
	ErrorMsg("cannot open pipe");
	goto end_formatting;
    }
    if(! fgets(parameters,100,pipe) )
    {
	ErrorMsg("getfdprm failed - format aborted");
	goto end_formatting;
    }		
    fclose(pipe);

//	printf("Parameters: %s\n", parameters);
    strcpy(name,"/dev/");
    strcat(name,drivenames[drive->GetSelection()]);
    sprintf(commandline,SETFDPRM" %s %s", name, parameters);
//    printf("commandline=%s\n", commandline);
    if(system(commandline) != 0)
	ErrorMsg("setfdprm failed - trying default settings");
   
    //system("/usr/bin/getfdprm /dev/fd0 >/dev/null");
   
    sprintf(commandline,SUPERFORMAT" -d%s -t%d -s%d ", 
    name, tracks_list[capacity->GetSelection()],
    sectors_list[capacity->GetSelection()]);
    if(! verifyflag->GetValue())
	strcat(commandline,"-f ");

    SetStatusText(commandline);
    pipe = mypopen(commandline,&child_pid);
    if(!pipe)
    {
	ErrorMsg("cannot call superformat");
	goto end_formatting;
    }
    for(;;)
    {
	while(myapp.Pending())
	    myapp.Dispatch();
	if((! feof(pipe)) && fscanf(pipe,"%256s",buffer) == 1)
	{
	    if(strcmp(buffer,"Formatting") == 0)
	    {
		fscanf(pipe,"%256s %d", buffer, &track);
		format_gauge->SetValue(track);
		sprintf(buffer,"Track %2d: formatting",track);
		SetStatusText(buffer);
	    }
	    else
	    if(strcmp(buffer,"Verifying") == 0)
	    {
		fscanf(pipe,"%256s %d", buffer, &track);
		verify_gauge->SetValue(track);
		sprintf(buffer,"Track %2d: verifying", track);
		SetStatusText(buffer);
	    }
	    else if(strcmp(buffer,"head") != 0
		    && strcmp(buffer,"0") != 0
		    && strcmp(buffer,"1") != 0
		    && strcmp(buffer,",") != 0) // none of these
	    {
		for(char *cptr = buffer; *cptr ; cptr++)
		    *cptr = (*cptr == '\n')  ? ' ' : *cptr;
		if(strcmp(buffer,"mformat")==0)
			ignore = 7;
		if(! ignore)
		{
			fprintf(stderr,"ERROR: <%s>\n",buffer);
			SetStatusText(buffer);
		}
		else
			ignore --;
	    }

	}
	else
	    break;
    }
    if(track != tracks-1)
	SetStatusText("ERROR: formatting failed.");
    
end_formatting:

    if(pipe)
	fclose(pipe);
    abort_button->Enable(FALSE);
    if(child_pid)
	kill(child_pid, SIGINT);
    child_pid = 0;
    quit_button->Enable(TRUE);
    format_button->Enable(TRUE);
    formatting = 0;
    
}

xfFrame::xfFrame(wxFrame *frame, int x, int y, int w, int h)
    :wxFrame(frame,"XFormat (C) 1997 by K. Ballüder", x,y,w,h)
{
    int nr;
    wxIcon *icon = new wxIcon(xformat_xpm);
    SetIcon(icon);


    formatting = 0;
    child_pid = 0;
    
    panel = new wxPanel(this);

    format_button =
	new wxButton(panel,(wxFunction)format_button_function,(char*)"FORMAT");
    abort_button =
	new wxButton(panel,(wxFunction)abort_button_function,"ABORT");
    new wxButton(panel,(wxFunction)info_button_function,"INFO");
    quit_button =
	new wxButton(panel,(wxFunction)quit_button_function,"QUIT");

    abort_button->Enable(FALSE);
    
    panel -> NewLine();
//    new wxMessage(panel,"XFormat (C) 1996 by Karsten Ballüder");
//    panel->NewLine();
//    new wxMessage(panel,"(karsten@piobelix.physik.uni-karlsruhe.de)");
//    panel->NewLine();

    for(nr = 0; drivenames[nr];nr++);
    drive =  new wxChoice(panel, (wxFunction)NULL,"Device",
			  -1,-1,-1,-1,
			  nr,drivenames);

    for(nr = 0; capacitynames[nr];nr++);
    capacity = new wxChoice(panel, (wxFunction) NULL,"Capacity",
			-1,-1,-1,-1,
			    nr, capacitynames);
    capacity->SetSelection(3);	// 1.44M

    panel->NewLine();

    mkfsflag = new wxCheckBox(panel, (wxFunction)NULL,"Make Filesystem");
    mkfsflag->SetValue(TRUE);
    for(nr = 0; filesystems[nr];nr++);
    fs = new wxChoice(panel, (wxFunction) NULL, "Filesystem", -1,-1,-1,-1,
		      nr, filesystems);
    verifyflag = new wxCheckBox(panel,(wxFunction)NULL,"Verify");
    verifyflag->SetValue(TRUE);
    panel -> NewLine();

    panel->SetLabelPosition(wxVERTICAL);
    format_gauge = new wxGauge(panel,"formatted", 100,
			       -1,-1,-1,40,wxHORIZONTAL);
    verify_gauge = new wxGauge(panel,"verified", 100,
			       -1,-1,-1,40,wxHORIZONTAL);

    CreateStatusLine(1);
    Show(TRUE);
}



wxFrame *xfApp :: OnInit(void)	/*	main 	*/
{

    signal(SIGCHLD,sigchld_handler);
    
    xfframe = new xfFrame(NULL, -1, -1,400,180);

            
    // Associate the menu bar with the frame
    xfframe->Show(TRUE);

    return xfframe;
}











