#include <alloca.h>
#include <ctype.h>
#include <linux/fs.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 "windows.h"

static int partFilter(struct partition * part) {
   int rc;

   if (doMount(part->device, "/mnt", "ext2", 0, 0)) {
       return 0;
   }

   if (access("/mnt/etc", R_OK) || access("/mnt/etc/fstab", R_OK) ||
       access("/mnt/proc", R_OK) || access("/mnt/bin", R_OK) ||
       access("/mnt/etc/redhat-release", R_OK)) {
       rc = 0;
   } else
       rc = 1;

   umount("/mnt");

   return rc;
}

int readMountTable(struct partitionTable table, struct fstab * finalFstab) {
   newtComponent okay, cancel, form, text, listbox, answer;
   int rootPartNum;
   struct partition * rootPart = NULL;
   FILE * f;
   char buf[200];
   char * start, * end;
   char device[50], mntpoint[50], type[50];
   struct fstab fstab;
   struct fstabEntry entry;
   int numParts;

   umountFilesystems(finalFstab);

   newtOpenWindow(10, 2, 55, 19, "Root Partition");
   text = newtTextbox(1, 1, 53, 2, NEWT_TEXTBOX_WRAP);
   newtTextboxSetText(text, "What partition holds the root partition "
                      "of your installation?");

   okay = newtButton(11, 15, "Ok");
   cancel = newtButton(33, 15, "Cancel");

   form = newtForm(NULL, NULL, 0);
   listbox = addPartitionListbox(table, form, 3, 4, 7, PART_EXT2,
                                 partFilter, &numParts);

   if (!numParts) {
       newtFormDestroy(form);

       /* try again, but don't be quite so picky */
       form = newtForm(NULL, NULL, 0);
       listbox = addPartitionListbox(table, form, 3, 4, 7, PART_EXT2,
                                     NULL, &numParts);
       if (!numParts) {
           newtFormDestroy(form);
           newtPopWindow();

           errorWindow("You don't have any Linux partitions. You "
                           "can't upgrade this system!");
           return INST_CANCEL;         /* INST_ERROR would hang everything */
       }
   }

   if (numParts == 1) {
       rootPart = newtListboxGetCurrent(listbox);
       rootPartNum = (rootPart - table.parts);
       logMessage("Using partition %s for the root device",
                   table.parts[rootPartNum].device);
       if (doMount(table.parts[rootPartNum].device, "/mnt", "ext2", 0, 0)) {
           errorWindow("Could not mount automatically selected device.");
           answer = NULL;
           return INST_ERROR;
       }
       answer = okay;
   } else {
       newtFormAddComponents(form, text, okay, cancel, NULL);

       do {
           answer = newtRunForm(form);
           if (answer == cancel) continue;

           rootPart = newtListboxGetCurrent(listbox);
           rootPartNum = (rootPart - table.parts);

           if (doMount(table.parts[rootPartNum].device, "/mnt", "ext2",
                       0, 0)) {
               errorWindow("Could not mount device.");
               answer = NULL;
               continue;
           }

           if (testing) {
               continue;
           }

           if (access("/mnt/etc", R_OK) || access("/mnt/etc/fstab", R_OK) ||
               access("/mnt/proc", R_OK) || access("/mnt/bin", R_OK)) {
               errorWindow("That doesn't appear to be a root partition.");
               umount("/mnt");
               answer = NULL;
           }
       } while (!answer);
   }

   newtFormDestroy(form);
   newtPopWindow();

   if (answer == cancel) return INST_CANCEL;

   if (testing)
       f = fopen("/etc/fstab", "r");
   else
       f = fopen("/mnt/etc/fstab", "r");

   if (!f) {
       errorWindow("Cannot read /mnt/etc/fstab: %s");
       return INST_ERROR;
   }

   memset(&fstab, 0, sizeof(fstab));

   while (fgets(buf, sizeof(buf) - 1, f)) {
       start = buf;
       while (*start && isspace(*start)) start++;
       if (!*start || *start == '#') continue;

       if (strncmp(start, "/dev/", 5)) {
           continue;
       }

       if (strstr(start, "noauto")) continue;

       end = start + strlen(start) - 1;
       while (isspace(*end)) end--;
       end++;
       *end = '\0';

       if (sscanf(start, "%s %s %s", device, mntpoint, type) != 3) {
           errorWindow("Bad line in /mnt/etc/fstab -- aborting");
           fclose(f);
           freeFstab(fstab);
           umount("/mnt");
           return INST_ERROR;
       }

       if (strncmp(device, "/dev/hd", 7) && strncmp(device, "/dev/sd", 7))
           continue;

       entry.device = strdup(device + 5);
       entry.size = 0;
       entry.isMounted = 0;
       entry.doFormat = 0;
       entry.mntpoint = strdup(mntpoint);

       if (!strcmp(type, "ext2")) {
           entry.type = PART_EXT2;
           entry.tagName = "Linux native";
       } else if (!strcmp(type, "swap")) {
           entry.type = PART_SWAP;
           entry.tagName = "Linux swap";
           if (canEnableSwap) {
               enableswap(entry.device, 0, 0);
               canEnableSwap = 0;
           }
       } else if (!strcmp(type, "msdos")) {
           entry.type = PART_DOS;
           entry.tagName = "DOS 16-bit >=32";
       } else if (!strcmp(type, "hpfs")) {
           entry.type = PART_HPFS;
           entry.tagName = "OS/2 HPFS";
       } else {
           entry.type = PART_OTHER;
           entry.tagName = "Unknown";
       }

       addFstabEntry(&fstab, entry);
   }

   fclose(f);

   umount("/mnt");

   freeFstab(*finalFstab);
   *finalFstab = fstab;

   return 0;
}