#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <newt.h>
#include <popt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <unistd.h>
#include <dirent.h>

#include "dns.h"
#include "fs.h"
#include "hd.h"
#include "intl.h"
#include "install.h"
#include "kickstart.h"
#include "log.h"
#include "methods.h"
#include "net.h"
#include "windows.h"
#include "urlmethod.h"
#include "scsi.h"
#include "smb.h"

/* This was split into two pieces to keep the initial install program small */

static void makeInstimageSymlinks(void);
static int cdromPrepare(struct installMethod * method, struct netInfo * netc,
		       struct intfInfo * intf, int forceSupp,
		       struct driversLoaded ** dl);
static int hdPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int useSupp,
		      struct driversLoaded ** dl);
#ifndef DISABLE_NETWORK
static int nfsPrepare(struct installMethod * method, struct netInfo * netc,
		       struct intfInfo * intf, int forceSupp,
		       struct driversLoaded ** dl);
static int urlPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int forceSupp,
		      struct driversLoaded ** dl);
#if 0 /* disable for now */
static int smbPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int useSupp,
		      struct driversLoaded ** dl);
#endif /* disable */
static int nfsGetSetup(char ** hostptr, char ** dirptr);
#endif
int floppyRoot(struct installMethod * method, struct netInfo * netc,
		       struct intfInfo * intf, int forceSupp,
		       struct driversLoaded ** dl);
#if 0 /* disabled */
static int totalMemory(void);			/* in K */
#endif

#ifndef DISABLE_NETWORK
#define CDROM_METHOD_NUM 0
#define NFS_METHOD_NUM 1
#define HD_METHOD_NUM 2
#define FTP_METHOD_NUM 3
#else
#define CDROM_METHOD_NUM 0
#define HD_METHOD_NUM 1
#endif

/* We have a loop a bit later which translates the 'Local CDROM' language */
static struct installMethod methods[] = {
    { "Local CDROM", 		"cdrom", 0, LOCAL, cdromPrepare, NULL, NULL,
		NULL, NULL, NULL },
#ifndef DISABLE_NETWORK
    { "NFS image", 		"nfs", 0, NETWORK, nfsPrepare, NULL, NULL,
		NULL, NULL, NULL },
#endif
    { "Hard drive",		"hd", 0, LOCAL, hdPrepare, NULL, NULL,
		NULL, NULL, NULL },
#ifndef DISABLE_NETWORK
    { "FTP", 			"ftp", 0, NETWORK, urlPrepare, NULL, NULL,
		NULL, NULL, NULL },
    { "HTTP", 			"http", 0, NETWORK, urlPrepare, NULL, NULL,
		NULL, NULL, NULL },
#endif
#if 0
#ifdef __i386__
    { "SMB image", 		"smb", 0, NETWORK, smbPrepare, NULL, NULL,
		NULL, NULL, NULL },
#endif
#endif
} ;
static int numMethods = sizeof(methods) / sizeof(struct installMethod);
static int isMounted = 0;

static void setConfig(char *first, char *firstData, ...) {
    char *env, *data;
    va_list ap;

    if (first && *first && firstData && *firstData)
	setenv(first, firstData, 1);
    
    va_start(ap, firstData);
    
    while ((env = va_arg(ap, char *))) {
	data = va_arg(ap, char *);
	if (data && *data)
	    setenv(env, data, 1);
    }
}

#if 0 /* not currently used */
static int totalMemory(void) {
    int fd;
    int bytesRead;
    char buf[4096];
    char * chptr, * start;
    int total = 0;

    fd = open("/proc/meminfo", O_RDONLY);
    if (fd < 0) {
	logMessage("failed to open /proc/meminfo: %s", strerror(errno));
	return 0;
    }

    bytesRead = read(fd, buf, sizeof(buf) - 1);
    if (bytesRead < 0) {
	logMessage("failed to read from /proc/meminfo: %s", strerror(errno));
	close(fd);
	return 0;
    }

    close(fd);
    buf[bytesRead] = '\0';

    chptr = buf;
    while (*chptr && !total) {
	if (*chptr != '\n' || strncmp(chptr + 1, "MemTotal:", 9)) {
	    chptr++;
	    continue;
	}

	start = ++chptr ;
	while (*chptr && *chptr != '\n') chptr++;

	*chptr = '\0';

	logMessage("found total memory tag: \"%s\"", start);
	
	while (!isdigit(*start) && *start) start++;
	if (!*start) {
	    logMessage("no number appears after MemTotal tag");
	    return 0;
	}

	chptr = start;
	while (*chptr && isdigit(*chptr)) {
	    total = (total * 10) + (*chptr - '0');
	    chptr++;
	}
    }

    logMessage("%d kB are available", total);

    return total;
}

#endif /* not currently used */

static int installMethodWindow(struct installMethod ** method,
			       int showSuppBox, int isLocal, int isNetwork,
			       int * forceSuppPtr) {
    int i, usedMethods = 0;
    newtComponent textbox, listbox, form, okay, back, result;
    newtGrid buttonBar, grid, middle;
    char forceSupp = showSuppBox ? '*' : ' ';

    textbox = newtTextboxReflowed(-1, -1, _("What type of media contains "
				  "the packages to be installed?"), 30, 10, 
				  0, 0);

    for (i = 0; i < numMethods; i++) {
	if ((isLocal && methods[i].type == LOCAL) ||
	    (isNetwork && methods[i].type == NETWORK))
	    usedMethods++;
    }

    listbox = newtListbox(-1, -1, usedMethods, NEWT_FLAG_RETURNEXIT);
    
    for (i = 0; i < numMethods; i++) {
	if ((isLocal && methods[i].type == LOCAL) ||
	    (isNetwork && methods[i].type == NETWORK))
	    newtListboxAddEntry(listbox, _(methods[i].name), methods + i);
    }

    buttonBar = newtButtonBar(_("Ok"), &okay, _("Back"), &back, NULL);
    
    middle = newtCreateGrid(1, 2);
    newtGridSetField(middle, 0, 0, NEWT_GRID_COMPONENT, listbox,
		     0, 0, 0, 0, 0, 0);

#ifdef __i386__
    if (expert || showSuppBox)
	newtGridSetField(middle, 0, 1, NEWT_GRID_COMPONENT,
			    newtCheckbox(-1, -1, _("Force supplemental disk"),
				         forceSupp, NULL, &forceSupp),
			 0, 1, 0, 0, NEWT_ANCHOR_LEFT, 0);
#endif

    grid = newtGridBasicWindow(textbox, middle, buttonBar);
    newtGridWrappedWindow(grid, _("Installation Method"));

    form = newtForm(NULL, 0, 0);
    newtGridAddComponentsToForm(grid, form, 1);
    newtGridFree(grid, 1);

    result = newtRunForm(form);
    newtPopWindow();
    if (result == back) {
	newtFormDestroy(form);
	return INST_CANCEL;
    }

    *method = newtListboxGetCurrent(listbox);
    newtFormDestroy(form);

    *forceSuppPtr = (forceSupp != ' ');

    return 0;
}

int chooseInstallMethod(int showSuppBox, int isLocal, int isNetwork,
			struct installMethod ** method, 
			struct netInfo * netc, struct intfInfo * intf, 
			struct driversLoaded ** dl) {
    int rc;
    int forceSupp = 0;

    if (kickstart) {
	if (!ksGetCommand(KS_CMD_CDROM, NULL, NULL, NULL)) {
	    *method = methods + CDROM_METHOD_NUM;
	    return (*method)->prepareImage((*method), netc, intf, 0, dl);
	} else if (!ksGetCommand(KS_CMD_HD, NULL, NULL, NULL)) {
	    *method = methods + HD_METHOD_NUM;
	    return (*method)->prepareImage((*method), netc, intf, 0, dl);
#ifndef DISABLE_NETWORK
	} else if (!ksGetCommand(KS_CMD_NFS, NULL, NULL, NULL)) {
	    *method = methods + NFS_METHOD_NUM;
	    return (*method)->prepareImage((*method), netc, intf, 0, dl);
	} else if (!ksGetCommand(KS_CMD_URL, NULL, NULL, NULL)) {
	    *method = methods + FTP_METHOD_NUM;
	    return (*method)->prepareImage((*method), netc, intf, 0, dl);
#endif
	} else {
	    logMessage("No kickstart method was specified.");
	    kickstart = 0;
	}
    }

    do {
	rc = installMethodWindow(method, showSuppBox, isLocal, isNetwork,
				 &forceSupp);
	if (rc) return rc;

	if ((*method)->prepareImage) {
	    rc = (*method)->prepareImage((*method), netc, intf, forceSupp, dl);
	    if (rc == INST_ERROR) return rc;
	}
    } while (rc);

    return 0;
}

#ifndef DISABLE_NETWORK
/* We could use newtWinEntries() here, if we didn't like those clever
   callbacks */
static int nfsGetSetup(char ** hostptr, char ** dirptr) {
    newtComponent form, okay, cancel, answer, text;
    newtComponent siteLabel, dirLabel;
    newtGrid buttons, entryArea, grid;
    struct nfsMountCallbackInfo cbInfo;
    char * reflowedText;
    int width, height;

    if (*hostptr) {
	cbInfo.serverVal = *hostptr;
	cbInfo.netpathVal = *dirptr;
    } else {
	cbInfo.serverVal = "";
	cbInfo.netpathVal = "";
    }

    form = newtForm(NULL, NULL, 0);
    buttons = newtButtonBar(_("Ok"), &okay, _("Back"), &cancel, NULL);

    reflowedText = newtReflowText(
		       _("Please enter the following information:\n"
		         "\n"
		         "    o the name or IP number of your NFS server\n"
		         "    o the directory on that server containing\n"
		         "      Red Hat Linux for your architecture"),
			 60, 10, 10, &width, &height);
    text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, reflowedText);

    free(reflowedText);

    entryArea = newtCreateGrid(2, 2);
    siteLabel = newtLabel(-1, -1, _("NFS server name:"));
    newtGridSetField(entryArea, 0, 0, NEWT_GRID_COMPONENT, siteLabel, 
			0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);
    dirLabel = newtLabel(-1, -1, _("Red Hat directory:"));
    newtGridSetField(entryArea, 0, 1, NEWT_GRID_COMPONENT, dirLabel, 
			0, 0, 0, 0, NEWT_ANCHOR_LEFT, 0);

    cbInfo.server = newtEntry(-1, -1, cbInfo.serverVal, 24, &cbInfo.serverVal, 
				NEWT_ENTRY_SCROLL);
    newtComponentAddCallback(cbInfo.server, nfsMountCallback, &cbInfo);
    cbInfo.netpath = newtEntry(-1, -1, cbInfo.netpathVal, 24, 
				&cbInfo.netpathVal, NEWT_ENTRY_SCROLL);
    cbInfo.mntpoint = NULL;

    newtGridSetField(entryArea, 1, 0, NEWT_GRID_COMPONENT, cbInfo.server, 
			1, 0, 0, 0, 0, 0);
    newtGridSetField(entryArea, 1, 1, NEWT_GRID_COMPONENT, cbInfo.netpath, 
			1, 0, 0, 0, 0, 0);

    grid = newtCreateGrid(1, 3);
    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
			0, 0, 0, 0, 0, 0);
    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, entryArea,
			0, 1, 0, 1, 0, 0);
    newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, buttons,
			0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);

    newtGridWrappedWindow(grid, _("NFS Setup"));

    newtGridFree(grid, 1);

    newtFormAddComponents(form, text, cbInfo.server, cbInfo.netpath, okay, 
			  cancel, dirLabel, siteLabel, NULL);

    answer = newtRunForm(form);
    if (answer == cancel) {
	newtFormDestroy(form);
	newtPopWindow();
	
	return INST_CANCEL;
    }

    if (*hostptr) free(*hostptr);
    if (*dirptr) free(*dirptr);
    *hostptr = strdup(cbInfo.serverVal);
    *dirptr = strdup(cbInfo.netpathVal);

    newtFormDestroy(form);
    newtPopWindow();

    return 0;
}

#endif /* ENABLE_NETWORK */

static void nukeOldModules(void)
{
    DIR * moddir;
    struct dirent * ent;

    chdir("/modules");
    moddir = opendir("/modules");
    if (!moddir)
	return;
    ent = readdir(moddir);
    while (ent) {
	unlink(ent->d_name);
	ent = readdir(moddir);
    }
    closedir(moddir);
    chdir("/");
    rmdir("/modules");
}

/* Creates the symlinks to NFS or CD-Rom images */
static void makeInstimageSymlinks(void)
{

    if (!access("/tmp/rhimage/RedHat/instimage/lib", X_OK)) {
	unlink("/tmp/rhimage/RedHat/instimage/lib");
	symlink("/tmp/rhimage/RedHat/instimage/lib", "/lib");
    }
    
    if (!access("/tmp/rhimage/RedHat/instimage/usr/bin", X_OK)) {
	unlink("/tmp/rhimage/RedHat/instimage/usr/bin");
	symlink("/tmp/rhimage/RedHat/instimage/usr/bin", "/usr/bin");
    }
    
    if (!access("/tmp/rhimage/RedHat/instimage/usr/etc", X_OK)) {
	unlink("/tmp/rhimage/RedHat/instimage/usr/etc");
	symlink("/tmp/rhimage/RedHat/instimage/usr/etc", "/usr/etc");
    }
    
    if (!access("/tmp/rhimage/RedHat/instimage/modules", X_OK)) {
	nukeOldModules();
	symlink("/tmp/rhimage/RedHat/instimage/modules", "/modules");
    }
}

static int cdromPrepare(struct installMethod * method, struct netInfo * netc,
		        struct intfInfo * intf, int useSupp, 
			struct driversLoaded ** dl) {
    char * cddev;
    int rc = 0;

    if (!kickstart)
	rc = newtWinChoice(_("Note"), _("Ok"), ("Back"),
			_("Insert your Red Hat CD into your CD drive now"));

    if (rc == 2) return INST_CANCEL;

    while (1) {
	/* this autoprobes already, so we don't need to do anything special
	   for the kickstart :-) */
	rc = setupCDdevice(&cddev, dl);
	if (rc) return rc;

	if ((rc = loadFilesystem("isofs", "iso9660", dl))) return rc;

	rc = doMount(cddev, "/tmp/rhimage", "iso9660", 1, 0);
	if (rc) {
	    removeCDmodule(dl);
	    newtWinMessage(_("Error"), _("Ok"), 
		    _("I could not mount a CD on device /dev/%s"), cddev);
	    rmdir("/tmp/rhimage");
	    return INST_CANCEL;
	}

	if (access("/tmp/rhimage/RedHat", R_OK)) {
	    umount("/tmp/rhimage");
	    rmdir("/tmp/rhimage");
	    removeCDmodule(dl);
	    newtWinMessage(_("Error"), _("Ok"), 
			   _("That CDROM device does "
			     "not seem to contain a Red Hat CDROM."));
	    return INST_CANCEL;
	}

	rc = 0;
	while (useSupp) {
	    rc = floppyRoot(NULL, NULL, NULL, 0, NULL);

	    logMessage("got floppy root rc=%d", rc);

	    if (rc == INST_CANCEL) {
		umount("/tmp/rhimage");
		break;
	    } else if (!rc) {
		logMessage("breaking out of loop");
		break;
	    }
	}

	if (!rc) break;
    }

    if (!useSupp) {
	makeInstimageSymlinks();
    }

    logMessage("done here");

    return 0;
}

#ifndef DISABLE_NETWORK

static int nfsPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int useSupp,
		      struct driversLoaded ** dl) {
    char * host = NULL, * dir = NULL;
    char * buf;
    enum { NFS_STEP_NET, NFS_STEP_INFO, NFS_STEP_MOUNT, NFS_STEP_DONE }
		step = NFS_STEP_NET;
    int rc;
    int ksArgc;
    char ** ksArgv;
    poptContext optCon;
    int direction = 1;
    struct poptOption ksNfsOptions[] = {
	    { "server", '\0', POPT_ARG_STRING, &host, 0 },
	    { "dir", '\0', POPT_ARG_STRING, &dir, 0 },
	    { 0, 0, 0, 0, 0 }
	};


    if (kickstart) {
	/* FIXME: there must be a better test to use here */
	if (!intf->set || !netc->set) 
	    if (bringUpNetworking(intf, netc, dl, 1)) return INST_ERROR;

	ksGetCommand(KS_CMD_NFS, NULL, &ksArgc, &ksArgv);

	optCon = poptGetContext(NULL, ksArgc, ksArgv, ksNfsOptions, 0);

	if ((rc = poptGetNextOpt(optCon)) < -1) {
	    newtWinMessage(_("nfs command"),  _("Ok"),
		       _("bad argument to kickstart nfs command %s: %s"),
		       poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
		       poptStrerror(rc));
	}

	if (!host || !dir) {
	    newtWinMessage(_("nfs command"),  _("Ok"),
		       _("nfs command incomplete"));
	} else {
	    step = NFS_STEP_MOUNT;
	}
    }

    while (step != NFS_STEP_DONE) {
	switch (step) {
	  case NFS_STEP_NET:
	    rc = bringUpNetworking(intf, netc, dl, direction);
	    if (rc) return rc;
	    direction = 1;
	    step = NFS_STEP_INFO;
	    break;

	  case NFS_STEP_INFO:
	    if (!host && (intf->set & INTFINFO_HAS_BOOTSERVER))
		host = mygethostbyaddr(inet_ntoa(intf->bootServer));
	    if (!dir && (intf->set & INTFINFO_HAS_BOOTFILE))
		dir = strdup(intf->bootFile);

	    rc = nfsGetSetup(&host, &dir);
	    if (rc == INST_CANCEL) {
		step = NFS_STEP_NET;
		direction = -1;
	    } else if (rc == INST_ERROR)
		return INST_ERROR;
	    else
		step = NFS_STEP_MOUNT;
	    break;

	  case NFS_STEP_MOUNT:
	    if (!strlen(host) || !strlen(dir))
		rc = INST_ERROR;
	    else {
		buf = malloc(strlen(host) + strlen(dir) + 10);
		strcpy(buf, host);
		strcat(buf, ":");
		strcat(buf, dir);

		if ((rc = loadFilesystem("nfs", "nfs", dl))) return rc;

		rc = doMount(buf, "/tmp/rhimage", "nfs", 1, 0);
		free(buf);
	    }

	    if (rc) {
		step = NFS_STEP_INFO;
		newtWinMessage(_("Error"), _("Ok"), 
		        _("I could not mount that directory from the server"));
	    } else {
	        if (access("/tmp/rhimage/RedHat", R_OK)) {
		    step = NFS_STEP_INFO;
		    newtWinMessage(_("Error"), _("Ok"), 
				   _("That directory does not seem to contain "
				     "a Red Hat installation tree."));
		    umount("/tmp/rhimage");
		} else
		    step = NFS_STEP_DONE;
	    }

	    rc = 0;
	    while (useSupp) {
		rc = floppyRoot(NULL, NULL, NULL, 0, NULL);

		if (rc == INST_CANCEL) {
		    umount("/tmp/rhimage");
		    break;
		} else if (!rc) {
		    break;
		}
	    }

	    if (rc) {
		umount("/tmp/rhimage");
		step = NFS_STEP_INFO;
	    }

	    break;

	  case NFS_STEP_DONE:
	    break;
	}
    }

    if (!kickstart) {
	free(host);
	free(dir);
    }

    if (!useSupp) {
	makeInstimageSymlinks();
    }

    return 0;
}

#endif

static int loadCompressedRamdisk(int fd, off_t size, char *title,
				 char *ramdisk)
{
    int rc = 0, ram, i;
    gzFile stream;
    char buf[1024];
    newtComponent form = NULL, scale = NULL;

    if (testing) return 0;

    stream = gzdopen (dup(fd), "r");

    strcpy(buf, "/tmp/");
    strcat(buf, ramdisk);
    
    if (devMakeInode(ramdisk, buf)) return 1;
    ram = open(buf, O_WRONLY);
    unlink(buf);

    if (title != NULL) {
	if (size > 0)
	    newtCenteredWindow(70, 5, "Loading");
	else
	    newtCenteredWindow(70, 3, "Loading");
	
	form = newtForm(NULL, NULL, 0);
	
	newtFormAddComponent(form, newtLabel(1, 1, title));
	if (size > 0) {
	    scale = newtScale(1, 3, 68, size);
	    newtFormAddComponent(form, scale);
	}
	newtDrawForm(form);
	newtRefresh();
    }
    for (i = 0; !gzeof(stream) && !rc; i++) {
	if (gzread(stream, buf, sizeof(buf)) != sizeof(buf)) {
	    if (!gzeof(stream)) {
		logMessage("error reading from device: %s", strerror(errno));
		rc = 1;
		break;
	    }
	}
	if (write(ram, buf, sizeof(buf)) != sizeof(buf)) {
	    logMessage("error writing to device: %s", strerror(errno));
	    rc = 1;
	}
	if (title != NULL && size > 0) {
	    newtScaleSet(scale, lseek(fd, 0L, SEEK_CUR));
	    newtRefresh();
	}
    }

    if (title != NULL) {
	newtPopWindow();
	newtFormDestroy(form);
    }
    
    close(ram);
    gzclose(stream);

    return rc;
}

int loadStage2Ramdisk(int fd, off_t size)
{
    int rc;
    
    rc = loadCompressedRamdisk(fd, size, _("Loading second stage ramdisk..."),
			       "ram3");
    
    if (rc) {
	errorWindow("Error loading ramdisk.");
	return rc;
    }
    
    if (doMount("ram3", "/tmp/image", "ext2", 1, 0)) {
	errorWindow("Error mounting ramdisk. This shouldn't "
		    "happen, and I'm rebooting your system now.");
	exit(1);
    }

    symlink("/tmp/image/lib", "/lib");
    symlink("/tmp/image/etc", "/etc");
    symlink("/tmp/image/usr/bin", "/usr/bin");
    symlink("/tmp/image/usr/etc", "/usr/etc");
    symlink("/tmp/image/sbin", "/sbin");
    nukeOldModules();
    symlink("/tmp/image/modules", "/modules");
    
    isMounted = 1;

    return 0;
}

int loadPCMCIADisk(void)
{
    int rc, fd;
    struct stat sb;
    
    if (testing) return 0;

    while (doMount("fd0", "/tmp/floppy", "ext2", 1, 0) ||
	   access("/tmp/floppy/pcmcia.img", R_OK) ||
	   (fd = open("/tmp/floppy/pcmcia.img", O_RDONLY)) < 0) {
	umount("/tmp/floppy");

	rc = newtWinChoice(_("PCMCIA Disk"), _("Ok"), _("Cancel"),
	    _("I failed to mount the floppy. Please insert the "
	      "Red Hat PCMCIA disk, or choose "
	      "Cancel to pick a different installation process."));
	if (rc == 2) return INST_CANCEL;
    }

    stat("/tmp/floppy/pcmcia.img", &sb);
    rc = loadCompressedRamdisk(fd, sb.st_size, _("Loading PCMCIA Support"),
			       "ram2");
    close(fd);
    umount("/tmp/floppy");
    
    if (rc) {
	errorWindow("Could not load PCMCIA ramdisk");
	umount("/tmp/floppy");
	return INST_ERROR;
    }

    umount("/tmp/floppy");
    
    if (doMount("ram2", "/tmp/pcmcia", "ext2", 1, 0)) {
	errorWindow("Error mounting ramdisk. This shouldn't "
		    "happen, and I'm rebooting your system now.");
	exit(1);
    }

    symlink("/tmp/pcmcia/sbin/cardmgr", "/sbin/cardmgr");
    unlink("/sbin/sh");
    symlink("/tmp/pcmcia/sbin/sh", "/sbin/sh");
    symlink("/tmp/pcmcia/etc/pcmcia", "/etc/pcmcia");
    
    return 0;
}


int loadFloppyRoot(struct installMethod * method) { 
    int rc, fd;
    struct stat sb;
    
    if (isMounted) return 0;

    if (testing) return 0;

    while (doMount("fd0", "/tmp/image", "ext2", 1, 0) ||
	   access("/tmp/image/stage2.img", R_OK) ||
	   (fd = open("/tmp/image/stage2.img", O_RDONLY)) < 0) {
	/* in case the mount succeeded */
	umount("/tmp/image");
	
	rc = newtWinChoice(_("Supplemental Disk"), _("Ok"), _("Cancel"),
	    _("I failed to mount the floppy. Please insert the "
	      "Red Hat Supplementary Install disk, or choose "
	      "Cancel to pick a different installation process."));
	if (rc == 2) return INST_CANCEL;
    }

    stat("/tmp/image/stage2.img", &sb);
    rc = loadCompressedRamdisk(fd, sb.st_size, _("Loading Supplemental Disk..."),
			       "ram3");
    close(fd);
    umount("/tmp/image");
    if (doMount("ram3", "/tmp/supp", "ext2", 1, 0)) {
	errorWindow("Error mounting ramdisk. This shouldn't "
		    "happen, and I'm rebooting your system now.");
	exit(1);
    }

    rc = newtWinChoice(_("Driver Disk"), _("Ok"), _("Back"),
		       _("This install method requires a driver disk. Please remove "
			 "the supplemental disk currently in your drive and replace it with "
			 "the Red Hat Modules disk."));
    if (rc == 2) return INST_CANCEL;

    
    while (doMount("fd0", "/tmp/moddisk", "ext2", 1, 0) ||
	   access("/tmp/image/modules.img", R_OK) ||
	   (fd = open("/tmp/image/modules.img", O_RDONLY)) < 0) {
	/* in case the mount succeeded */
	umount("/tmp/image");
	
	rc = newtWinChoice(_("Driver Disk"), _("Ok"), _("Cancel"),
	    _("I failed to mount the floppy. Please insert the "
	      "Red Hat Module disk, or choose "
	      "Cancel to pick a different installation process."));
	if (rc == 2) return INST_CANCEL;
    }

    stat("/tmp/floppy/modules.img", &sb);
    rc = loadCompressedRamdisk(fd, sb.st_size, _("Loading Driver Disk..."),
			       "ram4");
    close(fd);
    umount("/tmp/image");
    
    if (doMount("ram4", "/tmp/modules", "ext2", 1, 0)) {
	errorWindow("Error mounting ramdisk. This shouldn't "
		    "happen, and I'm rebooting your system now.");
	exit(1);
    }
    
    symlink("/tmp/supp/lib", "/lib");
    symlink("/tmp/supp/etc", "/etc");
    symlink("/tmp/supp/usr/bin", "/usr/bin");
    symlink("/tmp/supp/usr/etc", "/usr/etc");
    nukeOldModules();
    symlink("/tmp/modules", "/modules");

    isMounted = 1;

    return 0;
}

int floppyRoot(struct installMethod * method, struct netInfo * netc,
	       struct intfInfo * intf, int forceSuppDisk,
	       struct driversLoaded ** dl) { 
    int rc;

    if (!access("/usr/bin/runinstall2", R_OK)) {
	isMounted = 1;
	return 0;
    }

    do {
	rc = newtWinChoice(_("Supplemental Disk"), _("Ok"), _("Back"),
		_("This install method requires two additional disks. Please remove "
		  "the boot disk currently in your drive and replace it with "
		  "the Red Hat Supplementary Install disk."));
	if (rc == 2) return INST_CANCEL;

	rc = loadFloppyRoot(method);
    } while (rc);

    return 0;
}

static int hdPrepare(struct installMethod * method, struct netInfo * netc,
		     struct intfInfo * intf, int useSupp,
		     struct driversLoaded ** dl) {
    newtComponent okay, cancel, form, text, listbox, label, answer, dirEntry;
    newtGrid partGrid, buttons, entryGrid, grid;
    int numParts;
    int i;
    char * type;
    char * dir = NULL;
    char * partname = NULL;
    char * dest;
    char * defaultDevice;
    char * defaultDir;
    char * reflowedText;
    int width, height;
    struct partition * part = NULL;
    int done = 0;
    struct hdinfo * hdi;
    int rc;
    int fd;
    struct partitionTable table;
    int ksArgc;
    char ** ksArgv;
    poptContext optCon;
    struct poptOption ksHDOptions[] = {
	    { "dir", '\0', POPT_ARG_STRING, &dir, 0 },
	    { "partition", '\0', POPT_ARG_STRING, &partname, 0 },
	    { 0, 0, 0, 0, 0 }
    };

    setupSCSIInterfaces(0, dl, kickstart, 1);
    
    if (method->data) {
	hdi = method->data;
	defaultDir = strdup(hdi->dir);
	defaultDevice = hdi->device;
    } else {
	defaultDir = strdup("/");
	defaultDevice = "";
    }

    findAllPartitions(NULL, &table);

    if (kickstart) {
	ksGetCommand(KS_CMD_HD, NULL, &ksArgc, &ksArgv);

	optCon = poptGetContext(NULL, ksArgc, ksArgv, ksHDOptions, 0);

	if ((rc = poptGetNextOpt(optCon)) < -1) {
	    newtWinMessage(_("hd command"),  _("Ok"),
		       _("bad argument to kickstart hd command %s: %s"),
		       poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
		       poptStrerror(rc));
	}

	if (!partname || !dir) {
	    newtWinMessage(_("hd command"),  _("Ok"),
			   _("hd command incomplete"));
	    kickstart = 0;
	} else {
	    part=0;
	    for (i = 0; i < table.count; i++) {
		if (!strcmp(partname, table.parts[i].device)) {
		    part = &table.parts[i];
		    break;
               }
	    }
	    /* check to see if partition was not found */
	    if (part == 0) {
		newtWinMessage(_("hd command"),  _("Ok"),
			       _("HD device %s not found"),
			       partname);
		kickstart = 0;
	    }
	}
    }
    
    while (!done) {
	if (!kickstart) {
	    reflowedText = newtReflowText(_("What partition and directory on that "
					    "partition hold the RedHat/RPMS and "
					    "RedHat/base directories?"),
					  62, 5, 5, &width, &height);
	    text = newtTextbox(-1, -1, width, height, NEWT_TEXTBOX_WRAP);
	    newtTextboxSetText(text, reflowedText);
	    free(reflowedText);
	    
	    addPartitionListbox(table, 4, PART_EXT2 | PART_DOS | PART_FAT32, 
				NULL, &numParts, &partGrid, &listbox);
	    
	    buttons = newtButtonBar(_("Ok"), &okay, _("Cancel"), &cancel, NULL);
	    
	    for (i = 0; i < table.count; i++) {
		if (!strcmp(table.parts[i].device, defaultDevice)) {
		    newtListboxSetCurrentByKey(listbox, table.parts + i);
		}
	    }
	    
	    label = newtLabel(-1, -1, _("Directory holding Red Hat:"));
	    dirEntry = newtEntry(28, 11, defaultDir, 28, &dir, NEWT_ENTRY_SCROLL);
	    
	    entryGrid = newtGridHStacked(NEWT_GRID_COMPONENT, label,
					 NEWT_GRID_COMPONENT, dirEntry,
					 NEWT_GRID_EMPTY);
	    
	    grid = newtCreateGrid(1, 4);
	    newtGridSetField(grid, 0, 0, NEWT_GRID_COMPONENT, text,
			     0, 0, 0, 1, 0, 0);
	    newtGridSetField(grid, 0, 1, NEWT_GRID_SUBGRID, partGrid,
			     0, 0, 0, 1, 0, 0);
	    newtGridSetField(grid, 0, 2, NEWT_GRID_SUBGRID, entryGrid,
			     0, 0, 0, 1, 0, 0);
	    newtGridSetField(grid, 0, 3, NEWT_GRID_SUBGRID, buttons,
			     0, 0, 0, 0, 0, NEWT_GRID_FLAG_GROWX);
	    
	    newtGridWrappedWindow(grid, _("Select Partition"));
	    
	    form = newtForm(NULL, NULL, 0);
	    newtGridAddComponentsToForm(grid, form, 1);
	    newtGridFree(grid, 1);
	    
	    answer = newtRunForm(form);
	    
	    if (answer != cancel) {
		part = newtListboxGetCurrent(listbox);
	    }

	    /* protect from form free */
	    dir = strdup(dir);
	    
	    newtFormDestroy(form);
	    newtPopWindow();
	    
	    if (answer == cancel) return INST_CANCEL;
	    
	} /* !kickstart */
	defaultDir = dir;
	defaultDevice = part->device;

	switch (part->type) {
	  case PART_EXT2:	type = "ext2"; 		break;
	  case PART_DOS:	type = "vfat"; 		break;
	  case PART_FAT32:	type = "vfat"; 		break;
	  default:	continue;
	}

	if (!strcmp(type, "vfat")) {
	    if ((rc = loadFilesystem("vfat", "vfat", dl))) return rc;
	}

	if (doMount(part->device, "/tmp/hdimage", type, 1, 0))
	    continue;

	unlink("/tmp/rhimage");
	
	/* the physical device is mounted on /tmp/hdimage, but all
	   access are through /tmp/rhimage which points to the RedHat
	   directory in /tmp/hdimage */

	dest = alloca(strlen(dir) + 20);
	sprintf(dest, "/tmp/hdimage/%s", dir);

	if (symlink(dest, "/tmp/rhimage")) {
	    newtWinMessage(_("Error"), _("Ok"), 
			   _("Failed to create /tmp/rhimage symlink: %s"), 
			   strerror(errno));
	    umount("/tmp/hdimage");
	    continue;
	} 

#ifdef __i386__
	if (access("/tmp/rhimage/RedHat/base/stage2.img", R_OK)) {
#else
	if (access("/tmp/rhimage/RedHat/base/hdlist", R_OK)) {
#endif	
	    newtWinMessage(_("Error"), _("Ok"), 
			_("Device %s does not appear to contain "
			  "a Red Hat installation tree."), part->device);
	    umount("/tmp/hdimage");
	    continue;
	} 

	done = 1; 

	if (method->data) {
	    hdi = method->data;
	    free(hdi->dir);
	}
    
	hdi = malloc(sizeof(*hdi));
	hdi->device = part->device;
	hdi->type = type;
	hdi->dir = strdup(dir);
	method->data = hdi;
	
#ifdef __i386__
	if (!useSupp) {
	    if ((fd = open("/tmp/rhimage/RedHat/base/stage2.img", O_RDONLY)) < 0) {
		newtWinMessage(_("Error"), _("Ok"), 
			       _("Error reading second stage ramdisk. "));
		return 1;
	    }
	    rc = loadStage2Ramdisk(fd, 0);
	    close(fd);
	    if (rc) {
		logMessage("ramdisk load failed.\n");
		return INST_ERROR;
	    }
	}
#endif
	setConfig("DEVICE", hdi->device, "TYPE", hdi->type,
		  "DIR", hdi->dir, NULL);
	
	umount("/tmp/hdimage");
	
    }
    return 0;
}

#ifndef DISABLE_NETWORK 
    
static int urlPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int useSupp,
		      struct driversLoaded ** dl) {
    struct iurlinfo ui;
    enum { URL_SETUP_NET, URL_SETUP_HOST1, URL_SETUP_HOST2, URL_SETUP_CHECK, 
		URL_SETUP_DONE } step = URL_SETUP_NET;
    int rc;
    FD_t fd;
    char doMore;
    urlprotocol protocol = URL_METHOD_FTP;
    char buf[256];
    int ksArgc;
    char ** ksArgv;
    poptContext optCon;
    char *url = NULL, *proxy = NULL, *proxyport = NULL;
    struct poptOption ksUrlOptions[] = {
	    { "url", '\0', POPT_ARG_STRING, &url, 0 },
	    { "proxy", '\0', POPT_ARG_STRING, &proxy, 0 },
	    { "proxyport", '\0', POPT_ARG_STRING, &proxyport, 0 },
	    { 0, 0, 0, 0, 0 }
	};

    if (!strncmp(method->abbrev, "ftp", 3))
	protocol = URL_METHOD_FTP;
    if (!strncmp(method->abbrev, "http", 4))
	protocol = URL_METHOD_HTTP;
    
    memset(&ui, 0, sizeof(ui));

    if (method->data)
	memcpy(&ui, method->data, sizeof(ui));
    else
	memset(&ui, 0, sizeof(ui));

    if (kickstart) {
	/* FIXME: there must be a better test to use here */
	if (!intf->set || !netc->set) 
	    if (bringUpNetworking(intf, netc, dl, 1)) return INST_ERROR;

	ksGetCommand(KS_CMD_URL, NULL, &ksArgc, &ksArgv);

	optCon = poptGetContext(NULL, ksArgc, ksArgv, ksUrlOptions, 0);

	if ((rc = poptGetNextOpt(optCon)) < -1) {
	    newtWinMessage(_("url command"),  _("Ok"),
		       _("bad argument to kickstart url command %s: %s"),
		       poptBadOption(optCon, POPT_BADOPTION_NOALIAS), 
		       poptStrerror(rc));
	}

	if (!url) {
	    newtWinMessage(_("url command"),  _("Ok"),
			   _("url command incomplete"));
	} else {
           if (ui.urlprefix) free(ui.urlprefix);
           ui.urlprefix = strdup(url);
	   if (proxy) {
	       if (ui.proxy) free(ui.proxy);
	       ui.proxy = strdup(proxy);
	       if (protocol == URL_METHOD_FTP)
		   addMacro(NULL, "_ftpproxy", NULL, ui.proxy, RMIL_RPMRC);
	       else
		   addMacro(NULL, "_httproxy", NULL, ui.proxy, RMIL_RPMRC);
	   }
	   if (proxyport) {
	       if (ui.proxyPort) free(ui.proxyPort);
	       ui.proxyPort = strdup(proxyport);
	       	if (protocol == URL_METHOD_FTP)
		    addMacro(NULL, "_ftpproxyport", NULL, ui.proxyPort,
			     RMIL_RPMRC);
		else
		    addMacro(NULL, "_httpproxyport", NULL, ui.proxyPort,
			     RMIL_RPMRC);
	   }
	   step = URL_SETUP_CHECK;
	}
    }

    while (step != URL_SETUP_DONE) {
	switch (step) {

	  case URL_SETUP_NET:
	    rc = bringUpNetworking(intf, netc, dl, 1);
	    if (rc) return rc;
	    step = URL_SETUP_HOST1;
	    break;

	  case URL_SETUP_HOST1:
	    rc = urlMainSetupPanel(&ui, protocol, &doMore);
	    if (rc == INST_ERROR) 
		return rc;
	    else if (rc)
		step = URL_SETUP_NET;
	    else if (doMore == ' ')
		step = URL_SETUP_CHECK;
	    else 
		step = URL_SETUP_HOST2;
	    break;

	  case URL_SETUP_HOST2:
	    rc = urlSecondarySetupPanel(&ui, protocol);
	    if (rc == INST_ERROR) 
		return rc;
	    else if (rc)
		step = URL_SETUP_HOST1;
	    else
		step = URL_SETUP_CHECK;
	    break;
	case URL_SETUP_CHECK:
#ifdef __i386__
	    if (!useSupp) {
		fd = urlinstStartTransfer(&ui, "base/stage2.img");
		
		if (fd == NULL || fdFileno(fd) < 0) {
		    newtPopWindow();
		    snprintf(buf, sizeof(buf), "%s/RedHat/base/stage2.img",
			     ui.urlprefix);
		    newtWinMessage(method->name, _("Ok"), 
				   _("Unable to retrieve the second stage ramdisk: %s"),
				   urlStrerror(buf));
		    ufdClose(fd);
		    step = URL_SETUP_HOST1;
		    break;
		}
		
		rc = loadStage2Ramdisk(fdFileno(fd), 0);
		urlinstFinishTransfer(fd);
		
		if (rc) {
		    logMessage("ramdisk load failed.\n");
		    step = URL_SETUP_HOST1;
		    break;
		}
	    }
#endif
	    
	    setConfig("HOST", ui.address, "PREFIX", ui.prefix,
		      "URLPREFIX", ui.urlprefix, "LOGIN", ui.login,
		      "PASSWORD", ui.password, "PROXY", ui.proxy,
		      "PROXYPORT", ui.proxyPort, NULL);
	    
	    step = URL_SETUP_DONE;
	    break;

	  case URL_SETUP_DONE:
	    break;
	}
    }

    if (method->data) free(method->data);
    method->data = malloc(sizeof(ui));
    memcpy(method->data, &ui, sizeof(ui));

    return 0;
}
#endif
#if 0 /* Disabled until we can get the new smb interface to the kernel
	 right */
#ifdef __i386__
static int smbPrepare(struct installMethod * method, struct netInfo * netc,
		      struct intfInfo * intf, int useSupp,
		      struct driversLoaded ** dl) {

    char * host = NULL, * dir = NULL, * acct = NULL, * pass = NULL;
    char * buf;
    static int moduleLoaded = 0;
    enum { SMB_STEP_NET, SMB_STEP_INFO, SMB_STEP_MOUNT, SMB_STEP_RAM,
	   SMB_STEP_DONE }
		step = SMB_STEP_NET;
    int rc, fd;
    struct stat sb;

    while (step != SMB_STEP_DONE) {
	switch (step) {
	  case SMB_STEP_NET:
	    rc = bringUpNetworking(intf, netc, dl, 1);
	    if (rc) return rc;
	    step = SMB_STEP_INFO;
	    break;

	  case SMB_STEP_INFO:
	    rc = smbGetSetup(&host, &dir, &acct, &pass);
	    if (rc == INST_CANCEL)
		step = SMB_STEP_NET;
	    else if (rc == INST_ERROR)
		return INST_ERROR;
	    else
		step = SMB_STEP_MOUNT;
	    break;

	  case SMB_STEP_MOUNT:
	    if (!strlen(host) || !strlen(dir))
		rc = INST_ERROR;
	    else {
		buf = malloc(strlen(host) + strlen(dir) + 10);
		strcpy(buf, host);
		strcat(buf, ":");
		strcat(buf, dir);

		if (!moduleLoaded) {
		    rc = loadModule("smbfs", DRIVER_FS, DRIVER_MINOR_NONE, dl);
		    if (rc) return rc;
		    moduleLoaded = 1;
		}

		rc = doPwMount(buf, "/tmp/rhimage", "smb", 1, 0, acct, pass);

		free(buf);
	    }

	    if (rc) {
		step = SMB_STEP_INFO;
		newtWinMessage("Error", "Ok", 
			"I could not mount that directory from the server");
	    } else {
	        if (access("/tmp/rhimage/RedHat", R_OK)) {
		    step = SMB_STEP_INFO;
		    newtWinMessage(_("Error"), _("Ok"), 
				 _("That directory does not seem "
				   "to contain a Red Hat installation tree."));
		    umount("/tmp/rhimage");
		} else
		    step = SMB_STEP_RAM;
	    }

	    break;
	case SMB_STEP_RAM:
	    if ((fd = open("/tmp/rhimage/RedHat/base/stage2.img",
			     O_RDONLY)) < 0) {
		newtWinMessage(_("Error"), _("Ok"), 
			       _("Error reading second stage ramdisk. "));
		return 1;
	    }
	    stat("/tmp/rhimage/RedHat/base/stage2.img", &sb);
	    rc = loadStage2Ramdisk(fd, sb.st_size);
	    close(fd);
	    if (rc) {
		logMessage("ramdisk load failed.\n");
		return INST_ERROR;
	    }
	  case SMB_STEP_DONE:
	    break;
	}
    }

    setConfig("HOST", host, "DIR", dir, "ACCT", acct, "PASS", pass, NULL);
    
    free(host);
    free(dir);

    return 0;
}
#endif
#endif /* DISABLE */
