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

#include "devices.h"
#include "fs.h"
#include "install.h"
#include "log.h"
#include "mkswap.h"
#include "perror.h"
#include "run.h"
#include "smb.h"
#include "windows.h"

int nfsmount(const char *spec, const char *node, int *flags,
	     char **extra_opts, char **mount_opts);

static int selectRootPartition(struct partitionTable table, int * rootPartNum);
static int fstabCmp(const void * a, const void * b);
static int mkdirChain(char * chain);
static int mkdirIfNone(char * directory);

int canEnableSwap = 1;
int badBlocks = 0;

char * nstrdup(const char * foo) {
    return foo ? strdup(foo) : NULL;
}

static int fstabCmp(const void * a, const void * b) {
    const struct fstabEntry * first = a;
    const struct fstabEntry * second = b;

    if (first->type != second->type) {
	if (first->type == PART_NFS)
	    return 1;
	else if (second->type == PART_NFS)
	    return -1;
    }

    return strcmp(first->mntpoint, second->mntpoint);
}

void fstabSort(struct fstab * fstab) {
    qsort(fstab->entries, fstab->numEntries, 
	  sizeof(*fstab->entries), fstabCmp);
}

void initFstabEntry(struct fstabEntry * e) {
    e->device = NULL;
    e->netHost = NULL;
    e->netPath = NULL;
    e->mntpoint = NULL;
    e->tagName = NULL;
    e->size = 0;
    e->type = PART_OTHER;
    e->isMounted = 0;
    e->doFormat = 0;
}

newtComponent addPartitionListbox(struct partitionTable table,
					 newtComponent form, int left, int top, 
					 int height, int type, 
					 int (*filter)(struct partition * part),
					 int * numItems) {
    newtComponent listbox, label;
    int i;
    char buf[80];

    listbox = newtListbox(left, top + 1, height, NEWT_LISTBOX_RETURNEXIT);

    label = newtLabel(left, top, "Device          Begin        End   "
			"Size (k)");

    if (numItems) *numItems = 0;

    for (i = 0; i < table.count; i++) {
	if (table.parts[i].type == type) {
	    if (!filter || filter(table.parts + i)) {
		sprintf(buf, "/dev/%-5s  %9d  %9d  %9d", table.parts[i].device, 
			table.parts[i].begin, table.parts[i].end, 
			table.parts[i].size);
		newtListboxAddEntry(listbox, buf, &table.parts[i]);

		if (numItems) (*numItems)++;
	    }
	}
    }

    newtFormAddComponents(form, label, listbox, NULL);

    return listbox;
}

static int selectRootPartition(struct partitionTable table, int * rootPartNum) {
    newtComponent okay, cancel, form, text, listbox, answer;
    int i;
    struct partition * rootPart = NULL;

    for (i = 0; i < table.count; i++) 
	if (table.parts[i].type == PART_EXT2) break;

    if (i == table.count) {
	newtWinMessage("Select Root", "Ok",
			"You don't have any Linux native partitions "
			"defined. You must return to repartition "
			"your hard drive.");
	
        return INST_CANCEL;
    }

    *rootPartNum = i;

    newtCenteredWindow(64, 19, "Select Root Partition");
    text = newtTextbox(1, 1, 62, 4, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, "The root partition forms the base of your Linux "
		       "filesystem. It must hold everything necessary for "
		       "booting and initializing your system. What partition "
		       "would you like to use for the root filesystem?");
    okay = newtButton(15, 15, "Ok");
    cancel = newtButton(38, 15, "Cancel");

    form = newtForm(NULL, NULL, 0);
    listbox = addPartitionListbox(table, form, 7, 6, 7, PART_EXT2, NULL, NULL);

    newtFormAddComponents(form, text, okay, cancel, NULL);
    answer = newtRunForm(form);
			
    if (answer != cancel) {
	rootPart = newtListboxGetCurrent(listbox);
    }

    newtFormDestroy(form);
    newtPopWindow();

    *rootPartNum = (rootPart - table.parts);
    if (!rootPart)
	return INST_CANCEL;
    else
	return 0;
}

static int badMountPoint(enum partitionTypes type, char * item) {
    char * chptr = item;

    if (*chptr != '/') {
	newtWinMessage("Bad Mount Point", "Ok",
		    "Mount points must begin with a leading /.");
	return INST_ERROR;
    } 

    if (*(chptr + 1) && *(chptr + strlen(chptr) - 1) == '/') {
	newtWinMessage("Bad Mount Point", "Ok",
		    "Mount points may not end with a /.");
	return INST_ERROR;
    } 

    while (*chptr && isprint(*chptr)) chptr++;

    if (*chptr) {
	newtWinMessage("Bad Mount Point", "Ok",
		    "Mount points may only printable characters.");
	return INST_ERROR;
    }

    if (type != PART_EXT2 && (
	 !strncmp(item, "/var", 4) ||
	 !strncmp(item, "/tmp", 4) ||
	 !strncmp(item, "/bin", 4) ||
	 !strncmp(item, "/sbin", 4) ||
	 !strncmp(item, "/etc", 4) ||
	 !strncmp(item, "/boot", 4) ||
	 !strncmp(item, "/dev", 4) ||
	 !strncmp(item, "/root", 4) ||
	 !strncmp(item, "/lib", 4))) {
	newtWinMessage("Bad Mount Point", "Ok",
		    "System partitions must be on Linux Native "
		    "partitions.");
	return INST_ERROR;
    }

    if (type != PART_EXT2 && type != PART_NFS &&
	!strncmp(item, "/usr", 4)) {
	newtWinMessage("Bad Mount Point", "Ok",
		    "/usr must be on a Linux Native partition "
		    "or an NFS volume.");
	return INST_ERROR;
    }

    return 0;
}

static char * restrdup(char * old, char * new) {
    if (old) free(old);
    if (new) return strdup(new); else return NULL;
}

void nfsMountCallback(newtComponent co, void * arg) {
    struct nfsMountCallbackInfo * nfsinfo = arg;
    char * chptr;
    char * copy;
 
    if (!strlen(nfsinfo->netpathVal)) {
	if (strchr(nfsinfo->serverVal, ':')) {
	    chptr = copy = alloca(strlen(nfsinfo->serverVal) + 1);
	    strcpy(copy, nfsinfo->serverVal);
	    chptr = strchr(copy, ':');
	    *chptr = '\0';
	    chptr++;
	    newtEntrySet(nfsinfo->server, copy, 1);
	    newtEntrySet(nfsinfo->netpath, chptr, 1);
	}
    }

    if (nfsinfo->mntpoint) {
	if (strlen(nfsinfo->netpathVal) && !strlen(nfsinfo->mntpointVal)) {
	    newtEntrySet(nfsinfo->mntpoint, nfsinfo->netpathVal, 1);
	}
    }
}

int editNetMountPoint(struct fstabEntry * item) {
    newtComponent form, server, path, point, okay, cancel, answer;
    char * pointValue, * pathValue, * serverValue;
    int done = 0;
    struct nfsMountCallbackInfo nfsinfo;
 
    newtCenteredWindow(50, 10, "Edit Network Mount Point");

    form = newtForm(NULL, NULL, 0);

    newtFormAddComponent(form, newtLabel(1, 1, "NFS Server  :"));
    newtFormAddComponent(form, newtLabel(1, 2, "NFS Path    :"));
    newtFormAddComponent(form, newtLabel(1, 3, "Mount point :"));

    server = newtEntry(17, 1, item->netHost, 20, &serverValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);
    path = newtEntry(17, 2, item->netPath, 20, &pathValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);
    point = newtEntry(17, 3, item->mntpoint, 20, &pointValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);

    nfsinfo.server = server;
    nfsinfo.mntpoint = point;
    nfsinfo.netpath = path;

    nfsinfo.serverVal = serverValue;
    nfsinfo.netpathVal = pathValue;
    nfsinfo.mntpointVal = pointValue;

    newtComponentAddCallback(server, nfsMountCallback, &nfsinfo);
    newtComponentAddCallback(path, nfsMountCallback, &nfsinfo);

    okay = newtButton(10, 6, "Ok");
    cancel = newtButton(30, 6, "Cancel");

    newtFormAddComponents(form, server, path, point, okay, cancel, NULL);

    do {
	answer = newtRunForm(form);

	if (answer == cancel) {
	    done = 1;
	} else if (*pointValue) {
	    if (!badMountPoint(item->type, pointValue)) 
		done = 1;
	}
    } while (!done);

    if (answer != cancel) {
	item->mntpoint = restrdup(item->mntpoint, pointValue);
	item->netPath = restrdup(item->netPath, pathValue);
	item->netHost = restrdup(item->netHost, serverValue);

	if (item->device) free(item->device);
	item->device = malloc(strlen(pathValue) + strlen(serverValue) + 5);
	sprintf(item->device, "%s:%s", serverValue, pathValue);
    }

    newtPopWindow();

    if (answer == cancel)
	return INST_CANCEL;
    return 0;
}	    

static void editDeviceMountPoint(struct fstabEntry * item) {
    newtComponent form, entry, okay, cancel, clear, answer;
    char buf[50];
    char * entryValue;
    int done = 0;
 
    newtCenteredWindow(50, 10, "Edit Mount Point");

    form = newtForm(NULL, NULL, 0);

    strcpy(buf,"Device      : /dev/");
    strcat(buf, item->device);
    newtFormAddComponent(form, newtLabel(1, 1, buf));
    newtFormAddComponent(form, newtLabel(1, 3, "Mount point :"));

    entry = newtEntry(17, 3, item->mntpoint, 20, &entryValue, 
		      NEWT_ENTRY_SCROLL | NEWT_ENTRY_RETURNEXIT);

    okay = newtButton(5, 6, "Ok");
    clear = newtButton(20, 6, "Clear");
    cancel = newtButton(35, 6, "Cancel");

    newtFormAddComponents(form, entry, okay, clear, cancel, NULL);

    do {
	newtFormSetCurrent(form, entry);
	answer = newtRunForm(form);

	if (answer == clear)
	    newtEntrySet(entry, "", 1);
	else if (answer == cancel || !*entryValue) {
	    done = 1;
	} else if (*entryValue) {
	    if (!badMountPoint(item->type, entryValue)) 
		done = 1;
	}
    } while (!done);

    if (answer != cancel) {
	item->mntpoint = restrdup(item->mntpoint, entryValue);
    }

    newtPopWindow();
}	    

void freeFstabEntry( struct fstabEntry *e ) {
    	if (e->mntpoint) free(e->mntpoint);
	if (e->device)   free(e->device);
	if (e->netPath)  free(e->netPath);
	if (e->netHost)  free(e->netHost);

}

void freeFstab(struct fstab fstab) {
    int i;

    for (i = 0; i < fstab.numEntries; i++) {
	freeFstabEntry( &fstab.entries[i] );
    }

    if (fstab.numEntries) free(fstab.entries);
}

struct fstab copyFstab(struct fstab * fstab) {
    struct fstab newfstab;
    int i, j;

    if (!fstab->numEntries) {
	newfstab.numEntries = 0;
	newfstab.entries = malloc(1);
	return newfstab;
    }

    /* duplicate the current fstab */
    newfstab.numEntries = fstab->numEntries;
    newfstab.entries = malloc(fstab->numEntries * sizeof(struct fstabEntry));
    for (i = j = 0; i < newfstab.numEntries; i++) {
	if (fstab->entries[i].mntpoint) {
	    newfstab.entries[j] = fstab->entries[i];
	    newfstab.entries[j].mntpoint = nstrdup(fstab->entries[i].mntpoint);
	    newfstab.entries[j].device = nstrdup(fstab->entries[i].device);
	    newfstab.entries[j].netPath = nstrdup(fstab->entries[i].netPath);
	    newfstab.entries[j].netHost = nstrdup(fstab->entries[i].netHost);
	    j++;
	}
    }

    newfstab.numEntries = j;

    /* return the memory we don't actually need */
    newfstab.entries = realloc(newfstab.entries, j * sizeof(struct fstabEntry));

    return newfstab;
}

int setupMountTable(struct partitionTable table, struct fstab * finalFstab,
		    struct netInterface * intf, struct netConfig * netc,
		    struct driversLoaded ** dl) {
    int rootPartNum = -1;
    int rc, hasdups;
    newtComponent f, okay, text, listbox, label, cancel, edit, answer;
    newtComponent addnfs;
    char buf[80];
    int i, j, numMountable, numLinux;
    int partNum;
    struct fstab fstab;
    int fstabNum;
    struct fstabEntry entry, * curr;
    int * fstabEntNum, * numptr;
    static int firstTime = 1;
    const char * basicFormat = "%-10s  %14s  %-20s %-18s";
    const char * devFormat = "/dev/%-5s  %14d  %-20s %-18s";
    const char * nfsFormat = "%-27s %-20s %-18s";

    fstab = copyFstab(finalFstab);

    if (firstTime) {
	/* First, make them pick a root partition */
	if ((rc = selectRootPartition(table, &rootPartNum))) {
	    freeFstab(fstab);
	    return rc;
	}
	firstTime = 0;

	for (i = partNum = 0; i < table.count; i++) {
	    if (i == rootPartNum) {
		initFstabEntry(&entry);
		entry.device = strdup(table.parts[i].device);
		entry.size = table.parts[i].size;
		entry.type = table.parts[i].type;
		entry.tagName = table.parts[i].tagName;
		entry.mntpoint = strdup("/");
		addFstabEntry(&fstab, entry);
		break;
	    }
	}

	freeFstab(*finalFstab);
	*finalFstab = copyFstab(&fstab);
    }    

    /* If only one mountable partition exists, it would have been set up
       as root above so we can stop here */
    numLinux = numMountable = 0;
    for (i = partNum = 0; i < table.count; i++) {
	if (table.parts[i].type == PART_EXT2)
	    numMountable++, numLinux++;
	else if (table.parts[i].type == PART_DOS ||
	         table.parts[i].type == PART_HPFS)
	    numMountable++;
    }
    if (!numMountable) {
	errorWindow("You don't have any mountable partitions!");
	return INST_ERROR;
    } else if (!numLinux) {
	errorWindow("You don't have any Linux partitions available!");
	return INST_ERROR;
    } else if (numMountable == 1)
	return 0;

    newtCenteredWindow(75, 19, "Partition Disk");

    f = newtForm(NULL, NULL, 0);
    text = newtTextbox(1, 1, 72, 4, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, "You may now mount other partitions within "
		       "your filesystem. Many users like to use separate "
		       "partitions for /usr and /home for example. You may "
		       "also mount your DOS or OS/2 partitions to make them "
		       "visible to Linux.");

    okay = newtButton(6, 15, "Ok");
    addnfs = newtButton(20, 15, "Add NFS");
    edit = newtButton(38, 15, "Edit");
    cancel = newtButton(54, 15, "Cancel");

    sprintf(buf, basicFormat, "Device", "Size", "Partition type", 
		"Mount point");
    label = newtLabel(1, 5, buf);

    listbox = newtListbox(1, 6, 8, NEWT_LISTBOX_RETURNEXIT);

    fstabEntNum = malloc(table.count * sizeof(*fstabEntNum));

    for (i = partNum = 0; i < table.count; i++) {
	if (table.parts[i].type == PART_EXT2 || 
	    table.parts[i].type == PART_DOS ||
	    table.parts[i].type == PART_HPFS) {

	    for (j = 0; j < fstab.numEntries; j++) 
		if (!strcmp(table.parts[i].device, fstab.entries[j].device))
		    break;

	    if (j < fstab.numEntries) {
		fstabNum = j;
	    } else {
		initFstabEntry(&entry);
		entry.device = strdup(table.parts[i].device);
		entry.size = table.parts[i].size;
		entry.type = table.parts[i].type;
		entry.tagName = table.parts[i].tagName;
		fstabNum = addFstabEntry(&fstab, entry);
	    }

	    sprintf(buf, devFormat, table.parts[i].device, 
		    table.parts[i].size, table.parts[i].tagName, 
		    fstab.entries[fstabNum].mntpoint ? 
			fstab.entries[fstabNum].mntpoint : "");
	    fstabEntNum[partNum] = fstabNum;
	    newtListboxAddEntry(listbox, buf, fstabEntNum + partNum);
	    partNum++;
	} 
    }

    newtFormAddComponents(f, text, label, listbox, okay, addnfs, edit, cancel, 
				NULL);

    do { 
	answer = newtRunForm(f);
	if (answer == listbox || answer == edit) {
	    numptr = newtListboxGetCurrent(listbox);
	    curr = fstab.entries + *numptr;

	    if (curr->type == PART_NFS) {
		editNetMountPoint(curr);
		sprintf(buf, nfsFormat, curr->device, curr->tagName,
			curr->mntpoint);
	    } else {
		editDeviceMountPoint(curr);
		sprintf(buf, devFormat, curr->device, curr->size, 
			curr->tagName, curr->mntpoint ? curr->mntpoint : "");
	    }

	    newtListboxSetEntry(listbox, numptr - fstabEntNum, buf);
	} else if (answer == addnfs) {
	    initFstabEntry(&entry);
	    entry.type = PART_NFS;
	    entry.tagName = "NFS Mount";
	    entry.device = NULL;
	    entry.mntpoint = NULL;

	    if (!intf->isConfigured) {
		rc = bringUpNetworking(intf, netc, dl);
	    } else {
		rc = 0;
	    }

	    if (!rc && !editNetMountPoint(&entry)) {
		fstabNum = addFstabEntry(&fstab, entry);
		fstabEntNum = realloc(fstabEntNum, 
				      sizeof(*fstabEntNum) * fstabNum);

		sprintf(buf, nfsFormat, entry.device, entry.tagName,
			entry.mntpoint);

		newtListboxAddEntry(listbox, buf, fstabEntNum + fstabNum);
		/*newtListboxSetCurrent(listbox, fstabNum);*/
	    }
	} else if (answer != cancel) {
	    answer = okay;
	    for (i = 0; i < fstab.numEntries; i++) 
		if (fstab.entries[i].mntpoint &&
		    !strcmp(fstab.entries[i].mntpoint, "/")) break;

	    if (i == fstab.numEntries) {
		newtWinMessage("No Root Partition", "Ok",
			    "You must assign a root (/) partition for the "
			    "install to proceed.");
		answer = NULL;
		continue;
	    }

	    hasdups = 0;
	    for (i = 0; i < fstab.numEntries; i++) 
		for (j = i + 1; j < fstab.numEntries; j++) 
		    if (fstab.entries[i].type != PART_SWAP &&
			fstab.entries[i].mntpoint &&
		        fstab.entries[j].mntpoint &&
			!strcmp(fstab.entries[i].mntpoint,
		                fstab.entries[j].mntpoint))
			hasdups = 1;

	    if (hasdups) {
		newtWinMessage("Duplicate Mounts", "Ok",
			    "You may not use the same mount point multiple "
			    "times.");
		answer = NULL;
	    }
	}
    } while (answer != okay && answer != cancel);

    newtFormDestroy(f);
    newtPopWindow();

    free(fstabEntNum);

    if (answer == cancel) { 
	freeFstab(fstab);
	return INST_CANCEL;
    }

    freeFstab(*finalFstab);
    *finalFstab = copyFstab(&fstab);
    freeFstab(fstab);

    /* Sort by mount point. This makes mounting everything in the proper
       order trivial */

    fstabSort(finalFstab);

    return 0;
}

static int mkExt2Filesystem(char * dev) {
    char * mke2fsargs[] = { "mke2fs", NULL, NULL, NULL};
    int rc;
    char message[80];

    mke2fsargs[1] = alloca(strlen(dev) + 6);
    strcpy(mke2fsargs[1], "/tmp/");
    strcat(mke2fsargs[1], dev);

    if (badBlocks)
	mke2fsargs[2] = "-c";

    sprintf(message, "Making ext2 filesystem on /dev/%s...", dev);
    winStatus(45, 3, "Running", message);

    devMakeInode(dev, mke2fsargs[1]);
    rc = runProgram(RUN_LOG, "/usr/bin/mke2fs", mke2fsargs);
    devRemoveInode(mke2fsargs[1]);

    newtPopWindow();

    if (rc)
	return INST_ERROR;
    else
	return 0;
}

int queryFormatFilesystems(struct fstab * fstab) {
    newtComponent form, checkList, okay, cancel, sb, text, answer;
    newtComponent checkbox;
    char * states;
    char doCheck = ' ';
    newtComponent * checks;
    char buf[80];
    int i, top;

    newtCenteredWindow(57, 19, "Format Partitions");

    form = newtForm(NULL, NULL, 0);

    if (fstab->numEntries > 4) 
	sb = newtVerticalScrollbar(47, 7, 4, 9, 10);
    else
	sb = NULL;

    checkList = newtForm(sb, NULL, 0);
    newtFormSetHeight(checkList, 4);

    if (sb)
	newtFormAddComponent(checkList, sb);

    text = newtTextbox(1, 1, 55, 6, NEWT_TEXTBOX_WRAP);
    newtTextboxSetText(text, "What partitions would you like to "
		       "format? We strongly suggest formatting all of the "
		       "system partitions, including /, /usr, and /var. There "
		       "is no need to format /home or /usr/local if they "
		       "have already been configured during a previous "
		       "install.");

    checks = alloca(sizeof(newtComponent) * fstab->numEntries);
    states = alloca(sizeof(char) * fstab->numEntries);
    for (i = 0, top = 0; i < fstab->numEntries; i++) {
	if (fstab->entries[i].doFormat)
	    states[i] = '*';
	else
	    states[i] = ' ';

	if (fstab->entries[i].type == PART_EXT2) {
	    sprintf(buf, "/dev/%-5s  %-33s", fstab->entries[i].device, 
		   fstab->entries[i].mntpoint);
	    checks[i] = newtCheckbox(3, 7 + top++, buf, states[i], NULL, 
				     &states[i]);
	    newtFormAddComponent(checkList, checks[i]);
	} else {
	    checks[i] = NULL;
	}
    }

    okay = newtButton(12, 14, "Ok");
    cancel = newtButton(34, 14, "Cancel");
    checkbox = newtCheckbox(10, 12, "Check for bad blocks during format",
				badBlocks ? '*' : ' ', NULL, &doCheck);
    
    newtFormAddComponents(form, text, checkList, checkbox, okay, cancel, NULL);

    answer = newtRunForm(form);

    newtFormDestroy(form);
    newtPopWindow();

    if (answer == cancel) return INST_CANCEL;

    for (i = 0; i < fstab->numEntries; i++) {
        if (states[i] != ' ') 
	    fstab->entries[i].doFormat = 1; 
    }

    if (doCheck == ' ')
	badBlocks = 0;
    else
	badBlocks = 1;

    return 0;
}

int formatFilesystems(struct fstab * fstab) {
    int i;

    for (i = 0; i < fstab->numEntries; i++) {
        if (fstab->entries[i].doFormat)
	    mkExt2Filesystem(fstab->entries[i].device);
    }

    return 0;
}

int doMount(char * dev, char * where, char * fs, int rdonly, int istty) {
    return doPwMount(dev, where, fs, rdonly, istty, NULL, NULL);
}

int doPwMount(char * dev, char * where, char * fs, int rdonly, int istty,
		char * acct, char * pw) { 
    char * buf = NULL;
    int isnfs = 0;
    char * mount_opt = NULL;
    long int flag;
    char * chptr;

    if (!strcmp(fs, "nfs")) isnfs = 1;

    logMessage("mounting %s on %s as type %s", dev, where, fs);

    if (testing) {
	newtWinMessage("Test mount", "Ok", "I would mount /dev/%s on %s",
		"using a(n) %s filesystem.", dev, where, fs);
    } else if (!strcmp(fs, "smb")) {
	mkdirChain(where);

	if (!acct) acct = "guest";
	if (!pw) pw = "";

	buf = alloca(strlen(dev) + 1);
	strcpy(buf, dev);
	chptr = buf;
	while (*chptr && *chptr != ':') chptr++;
	if (!*chptr) {
	    logMessage("bad smb mount point %s", where);
	    return 0;
	} 
	
	*chptr = '\0';
	chptr++;

#ifdef __i386__
	logMessage("mounting smb filesystem from %s path %s on %s",
			buf, chptr, where);
	return smbmount(buf, chptr, acct, pw, "localhost", where);
#else 
	errorWindow("smbfs only works on Intel machines");
#endif
    } else {
	mkdirChain(where);

  	if (!isnfs && *dev == '/') {
	    buf = dev;
	} else if (!isnfs) {
	    buf = alloca(200);
	    strcpy(buf, "/tmp/");
	    strcat(buf, dev);

	    if (devMakeInode(dev, buf)) return 1;
	} else {
	    char * junk = NULL;
	    int morejunk = 0;

	    buf = dev;
	    logMessage("calling nfsmount(%s, %s, &morejunk, &junk, &mount_opt)",
			buf, where);

	    if (nfsmount(buf, where, &morejunk, &junk, &mount_opt))
		logMessage("\tnfsmount returned non-zero");
	}

	flag = MS_MGC_VAL;
	if (rdonly)
	    flag |= MS_RDONLY;
	
	logMessage("calling mount(%s, %s, %s, %ld, %p)", buf, where, fs, 
			flag, mount_opt);

	if (mount(buf, where, fs, flag, mount_opt)) {
	    if (istty) {
		fprintf(stderr, "mount failed: %s\n", strerror(errno));
	    } else {
		newtWinMessage("Error", "Ok", perrorstr("mount failed"));
	    }
	    return 1;
	}

	if (!isnfs) devRemoveInode(buf);
    }

    return 0;
}

int mountFilesystems(struct fstab * fstab) {
    int i;
    char buf[1000];

    /* don't bother mounting odd (non-ext2) filesystems - we don't need
       them for installs */

    /* what about NFS? we should probably mount them to check mount integrity,
       but we don't know if networking is working well enough for us to do
       this */
   
    chdir("/");

    for (i = 0; i < fstab->numEntries; i++) {
	strcpy(buf, "/mnt");
	strcat(buf, fstab->entries[i].mntpoint);

	if (fstab->entries[i].type == PART_EXT2 ||
	    fstab->entries[i].type == PART_NFS ) {
	    if (fstab->entries[i].type == PART_EXT2 &&
		!doMount(fstab->entries[i].device, buf, "ext2", 0, 0))
		fstab->entries[i].isMounted = 1;
	    else if (fstab->entries[i].type == PART_NFS &&
		!doMount(fstab->entries[i].device, buf, "nfs", 0, 0))
		fstab->entries[i].isMounted = 1;
	    else {
		logMessage("unmounting all filesystems due to mount error");
		umountFilesystems(fstab);
		return INST_ERROR;
	    }
	} else {
	    logMessage("creating directory %s", buf);
	    mkdirChain(buf);
	}
    }

    return 0;
}

int umountFilesystems(struct fstab * fstab) {
    char buf[1000];
    int i;
    int olderrno;

    logMessage("unmounting all filesystems");

    chdir("/");

    if (testing) return 0;

    for (i = fstab->numEntries - 1; i >= 0; i--) {
	if (fstab->entries[i].isMounted) {
	    strcpy(buf, "/mnt");
	    strcat(buf, fstab->entries[i].mntpoint);

	    fstab->entries[i].isMounted = 0;
	    if (umount(buf)) {
		olderrno = errno;
		logMessage("error unmounting %s: %s\n", buf, strerror(errno));
		errno = olderrno;
		errorWindow("error unmounting filesystem: %s");
	    }
	}    
    }

    return 0;
}

static int mkdirChain(char * origChain) {
    char * chain;
    char * chptr;

    chain = alloca(strlen(origChain) + 1);
    strcpy(chain, origChain);
    chptr = chain;

    if (testing) return 0;

    while ((chptr = strchr(chptr, '/'))) {
	*chptr = '\0';
	if (mkdirIfNone(chain)) {
	    *chptr = '/';
	    return INST_ERROR;
	}

	*chptr = '/';
	chptr++;
    }

    if (mkdirIfNone(chain))
	return INST_ERROR;

    return 0;
}

static int mkdirIfNone(char * directory) {
    int rc, mkerr;
    char * chptr;

    /* If the file exists it *better* be a directory -- I'm not going to
       actually check or anything */
    if (!access(directory, X_OK)) return 0;

    /* if the path is '/' we get ENOFILE not found" from mkdir, rather
       then EEXIST which is weird */
    for (chptr = directory; *chptr; chptr++)
        if (*chptr != '/') break;
    if (!*chptr) return 0;

    rc = mkdir(directory, 0755);
    mkerr = errno;

    logMessage("creating directory %s rc = %d", directory, rc);

    if (!rc || mkerr == EEXIST) return 0;

    logMessage("    error: %s", strerror(mkerr));

    return INST_ERROR;
}

int writeFstab(struct fstab * fstab) {
    int i;
    FILE * f;
    int fd;
    char * fs = NULL;
    int freq = 0;
    int passno = 0;
    int bad;
    char buf[4096];
    char * chptr, * cddev = NULL;
    char * devFormat = "/dev/%-18s %-23s %-7s %-15s %d %d\n";
    char * nfsFormat = "%-23s %-23s %-7s %-15s %d %d\n";
    char * procFormat = "%-23s %-23s %-7s %-15s %d %d\n";
    char * options;
    char * format;

    logMessage("scanning /proc/mounts for iso9660 filesystems");
    fd = open("/proc/mounts", O_RDONLY);
    if (fd < 0) {
	logMessage("\terror opening /proc/mounts -- skipping check: %s",
			strerror(errno));
    } else {
	i = read(fd, buf, sizeof(buf) - 1);
	if (i < 0) {
	    logMessage("\terror reading /proc/mounts -- skipping check: %s",
			    strerror(errno));
	} else {
	    buf[i] = 0;
	    if ((chptr = strstr(buf, "iso9660"))) {
		chptr--;

		/* skip the mount point */
		while (*chptr == ' ') chptr--;
		while (*chptr != ' ') chptr--;
		while (*chptr == ' ') chptr--;

		chptr++;
		*chptr = '\0';
		while (*chptr != '/') chptr--;
		cddev = strdup(chptr + 1);
		
		logMessage("found mounted cdrom drive %s", cddev);
	    }
	}
    }

    #ifndef __sparc__
    if (!cddev) {
	if (findAtapi(&cddev)) cddev = NULL;
    }
    #endif
    if (!cddev) {
	if (findSCSIcdrom(&cddev)) cddev = NULL;
    }

    if (testing) {
	if (cddev) free(cddev);
	return 0;
    }

    logMessage("touching /etc/mtab");
    f = fopen("/mnt/etc/mtab", "w+");
    if (!f) {
	errorWindow("error touching /mnt/etc/mtab");
    } else {
	fclose(f);
    }

    logMessage("creating /etc/fstab");

    f = fopen("/mnt/etc/fstab", "w");
    if (!f) {
	if (cddev) free(cddev);
	errorWindow("error creating /mnt/etc/fstab");
	return INST_ERROR;
    }

    for (i = 0; i < fstab->numEntries; i++) {
	if (!fstab->entries[i].mntpoint) continue;

	passno = 0;
	freq = 0;
	format = devFormat;
        options = "defaults";

	bad = 0;
	switch (fstab->entries[i].type) {
	  case PART_EXT2:
	    freq = 1;
	    fs = "ext2";
	    if (!strcmp(fstab->entries[i].mntpoint, "/"))
		passno = 1;
	    else
		passno = 2;
	  
	    break;

	  case PART_NFS:
	    fs = "nfs";
	    options = "ro";
	    format = nfsFormat;
	    break;

	  case PART_SWAP:
	    fs = "swap";
	    break;

	  case PART_DOS:
	    fs = "msdos";
	    break;

	  case PART_HPFS:
	    fs = "hpfs";
	    break;

	  default:
	    bad = 1;
	}

	if (!bad)
	    fprintf(f, format, fstab->entries[i].device, 
		    fstab->entries[i].mntpoint, fs, options, freq, passno);
    }

    fprintf(f, devFormat, "fd0", "/mnt/floppy", "ext2", "noauto", 0, 0);
    if (cddev) {
	if (mkdir("/mnt/mnt/cdrom", 0755)) 
	    logMessage("failed to mkdir /mnt/mnt/cdrom: %s", strerror(errno));

	if (symlink(cddev, "/mnt/dev/cdrom"))
	    logMessage("failed to symlink /mnt/dev/cdrom: %s", strerror(errno));

	fprintf(f, devFormat, "cdrom", "/mnt/cdrom", "iso9660", "noauto,ro", 
		0, 0);
	free(cddev);
    }

    fprintf(f, procFormat, "none", "/proc", "proc", "defaults", 0, 0);

    fclose(f);


    return 0;
}

int addFstabEntry(struct fstab * fstab, struct fstabEntry entry) {
    int i;

    for (i = 0; i < fstab->numEntries; i++) 
	if (!strcmp(entry.device, fstab->entries[i].device))
	    break;

    if (i == fstab->numEntries) {
	fstab->numEntries++;
	if (fstab->numEntries > 1)
	    fstab->entries = realloc(fstab->entries, 
				sizeof(entry) * fstab->numEntries);
	else
	    fstab->entries = malloc(sizeof(entry));
    }

    fstab->entries[i] = entry;

    return i;
}

int activeSwapSpace(struct partitionTable * table, struct fstab * finalFstab) {
    newtComponent form, checkList, okay, cancel, sb, text, answer, label;
    newtComponent check, checkbox;
    char * states;
    char buf[80];
    int i, top, j, rc;
    struct fstabEntry entry;
    struct fstabEntry ** entries;
    struct fstab fstab;
    static int firstTime = 1;
    static int chkBadBlocks = 1;
    char doCheck = ' ';

    fstab = copyFstab(finalFstab);    

    form = newtForm(NULL, NULL, 0);

    for (i = j = 0; i < table->count; i++)
	if (table->parts[i].type == PART_SWAP)
	    j++;

    if (!j) {
	rc = newtWinChoice("No Swap Space", "Repartition", "Continue",
			    "You don't have any swap space defined. Would "
			    "you like to continue, or repartition your disk?");
	if (rc != 1)
	   return INST_CANCEL;
	
	return 0;
    }

    if (!kickstart) {
	newtCenteredWindow(54, 15, "Active Swap Space");
	    
	if (j > 3)
	    sb = newtVerticalScrollbar(47, 7, 3, 9, 10);
	else
	    sb = NULL;

	checkList = newtForm(sb, NULL, 0);
	newtFormSetHeight(checkList, 3);

	if (sb)
	    newtFormAddComponent(checkList, sb);

	text = newtTextbox(1, 1, 52, 4, NEWT_TEXTBOX_WRAP);
	newtTextboxSetText(text, "What partitions would you like to "
			   "use for swap space? This will destroy any "
			   "information already on the partition.");

	label = newtLabel(4, 5, "  Device          Begin        End   Size (k)");

	states = alloca(sizeof(char) * table->count);
	entries = alloca(sizeof(*entries) * table->count);

	for (i = 0, top = 0; i < table->count; i++) {
	    if (table->parts[i].type != PART_SWAP) continue;

	    for (j = 0; j < fstab.numEntries; j++) 
		if (!strcmp(table->parts[i].device, fstab.entries[j].device))
		    break;

	    if ((j < fstab.numEntries && fstab.entries[j].mntpoint) || 
		(firstTime && !testing))
		states[i] = '*';
	    else
		states[i] = ' ';

	    if (j < fstab.numEntries) 
		entries[i] = fstab.entries + j;
	    else
		entries[i] = NULL;

	    sprintf(buf, "/dev/%-5s  %9d  %9d  %9d", table->parts[i].device, 
		    table->parts[i].begin, table->parts[i].end, 
		    table->parts[i].size);
	    check = newtCheckbox(2, 6 + top++, buf, states[i], NULL, 
				     &states[i]);
	    newtFormAddComponent(checkList, check);
	}

	firstTime = 0;

	checkbox = newtCheckbox(2, 9, "Check for bad blocks during format",
				    chkBadBlocks ? '*' : ' ', NULL, &doCheck);

	okay = newtButton(9, 11, "Ok");
	cancel = newtButton(28, 11, "Cancel");
	
	newtFormAddComponents(form, text, label, checkList, checkbox, okay, 
			      cancel, NULL);

	answer = newtRunForm(form);

	newtFormDestroy(form);
	newtPopWindow();

	chkBadBlocks = (doCheck != ' ');

	if (answer == cancel) {
	    freeFstab(fstab);
	    return INST_CANCEL;
	}
    }

    for (i = 0; i < table->count; i++) {
	if (table->parts[i].type != PART_SWAP) continue;

        if (kickstart || states[i] != ' ') {
	    if (!kickstart && entries[i])
		entries[i]->mntpoint = strdup("swap");
	    else {
		initFstabEntry(&entry);
		entry.device = strdup(table->parts[i].device);
		entry.size = table->parts[i].size;
		entry.type = table->parts[i].type;
		entry.tagName = table->parts[i].tagName;
		entry.mntpoint = strdup("swap");

		addFstabEntry(&fstab, entry);
	    }
	} else if (entries[i]) {
	    free(entries[i]->mntpoint);
	    entries[i]->mntpoint = NULL;
	}
    }

    if (canEnableSwap) {
	for (i = 0; i < fstab.numEntries; i++) {
	    if (fstab.entries[i].type == PART_SWAP &&
		fstab.entries[i].mntpoint) {
		enableswap(fstab.entries[i].device, 0, chkBadBlocks);
		canEnableSwap = 0;
	    }
	}
    }

    freeFstab(*finalFstab);
    *finalFstab = copyFstab(&fstab);
    freeFstab(fstab);

    return 0;
}