GPT fdisk 0.5.0

Added several features, including a restructuring of the menu system,
GPT-to-MBR conversion, and the ability to re-read the MBR to generate
a fresh GPT from the current on-disk MBR.
diff --git a/gdisk.cc b/gdisk.cc
index dcc2c51..a69d3b4 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -17,25 +17,27 @@
 
 // Function prototypes....
 // int ReadPartitions(char* filename, struct GPTData* theGPT);
-int DoCommand(char* filename, struct GPTData* theGPT);
+void MainMenu(char* filename, struct GPTData* theGPT);
 void ShowCommands(void);
+void ExpertsMenu(char* filename, struct GPTData* theGPT);
 void ShowExpertCommands(void);
-int ExpertsMenu(char* filename, struct GPTData* theGPT);
+void RecoveryMenu(char* filename, struct GPTData* theGPT);
+void ShowRecoveryCommands(void);
 
 int main(int argc, char* argv[]) {
    GPTData theGPT;
    int doMore = 1;
    char* device = NULL;
 
-   printf("GPT fdisk (gdisk) version 0.4.2\n\n");
+   printf("GPT fdisk (gdisk) version 0.5.0\n\n");
 
     if (argc == 2) { // basic usage
       if (SizesOK()) {
          doMore = theGPT.LoadPartitions(argv[1]);
-         while (doMore) {
-            doMore = DoCommand(argv[1], &theGPT);
-         } // while
-      } // if
+         if (doMore) {
+            MainMenu(argv[1], &theGPT);
+         } // if (doMore)
+      } // if (SizesOK())
    } else if (argc == 3) { // usage with "-l" option
       if (SizesOK()) {
          if (strcmp(argv[1], "-l") == 0) {
@@ -55,112 +57,242 @@
    } // if/else
 } // main
 
-// Accept a command and execute it. Returns 0 if the command includes
-// an exit condition (such as a q or w command), 1 if more commands
-// should be processed.
-int DoCommand(char* filename, struct GPTData* theGPT) {
-   char command, line[255];
-   int retval = 1;
+// Accept a command and execute it. Returns only when the user
+// wants to exit (such as after a 'w' or 'q' command).
+void MainMenu(char* filename, struct GPTData* theGPT) {
+   char command, line[255], buFile[255];
+   int goOn = 1;
    PartTypes typeHelper;
    uint32_t temp1, temp2;
 
-   printf("\nCommand (m for help): ");
-   fgets(line, 255, stdin);
-   sscanf(line, "%c", &command);
-   switch (command) {
-      case 'b': case 'B':
-         theGPT->XFormDisklabel();
-         break;
-      case 'c': case 'C':
-         if (theGPT->GetPartRange(&temp1, &temp2) > 0)
-            theGPT->SetName(theGPT->GetPartNum());
-         else
-            printf("No partitions\n");
-         break;
-      case 'd': case 'D':
-         theGPT->DeletePartition();
-         break;
-      case 'i': case 'I':
-         theGPT->ShowDetails();
-         break;
-      case 'l': case 'L':
-         typeHelper.ShowTypes();
-         break;
-      case 'n': case 'N':
-         theGPT->CreatePartition();
-         break;
-      case 'o': case 'O':
-         printf("This option deletes all partitions and creates a new "
-                "protective MBR.\nProceed? ");
-         if (GetYN() == 'Y') {
-            theGPT->ClearGPTData();
-            theGPT->MakeProtectiveMBR();
-         } // if
-         break;
-      case 'p': case 'P':
-         theGPT->DisplayGPTData();
-	 break;
-      case 'q': case 'Q':
-         retval = 0;
-	 break;
-      case 's': case 'S':
-         theGPT->SortGPT();
-         printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n");
-         break;
-      case 't': case 'T':
-         theGPT->ChangePartType();
-         break;
-      case 'v': case 'V':
-         if (theGPT->Verify() > 0) { // problems found
-            printf("You may be able to correct the problems by using options on the experts\n"
-                   "menu (press 'x' at the command prompt). Good luck!\n");
-         } // if
-         break;
-      case 'w': case 'W':
-         if (theGPT->SaveGPTData() == 1)
-            retval = 0;
-         break;
-      case 'x': case 'X':
-         retval = ExpertsMenu(filename, theGPT);
-         break;
-      default:
-         ShowCommands();
-         break;
-   } // switch
-   return (retval);
-} // DoCommand()
+   do {
+      printf("\nCommand (? for help): ");
+      fgets(line, 255, stdin);
+      sscanf(line, "%c", &command);
+      switch (command) {
+         case 'b': case 'B':
+            printf("Enter backup filename to save: ");
+            fgets(line, 255, stdin);
+            sscanf(line, "%s", &buFile);
+            theGPT->SaveGPTBackup(buFile);
+            break;
+         case 'c': case 'C':
+            if (theGPT->GetPartRange(&temp1, &temp2) > 0)
+               theGPT->SetName(theGPT->GetPartNum());
+            else
+               printf("No partitions\n");
+            break;
+         case 'd': case 'D':
+            theGPT->DeletePartition();
+            break;
+         case 'i': case 'I':
+            theGPT->ShowDetails();
+            break;
+         case 'l': case 'L':
+            typeHelper.ShowTypes();
+            break;
+         case 'n': case 'N':
+            theGPT->CreatePartition();
+            break;
+         case 'o': case 'O':
+            printf("This option deletes all partitions and creates a new "
+                  "protective MBR.\nProceed? ");
+            if (GetYN() == 'Y') {
+               theGPT->ClearGPTData();
+               theGPT->MakeProtectiveMBR();
+            } // if
+            break;
+         case 'p': case 'P':
+            theGPT->DisplayGPTData();
+            break;
+         case 'q': case 'Q':
+            goOn = 0;
+            break;
+         case 'r': case 'R':
+            RecoveryMenu(filename, theGPT);
+            goOn = 0;
+            break;
+         case 's': case 'S':
+            theGPT->SortGPT();
+            printf("You may need to edit /etc/fstab and/or your boot loader configuration!\n");
+            break;
+         case 't': case 'T':
+            theGPT->ChangePartType();
+            break;
+         case 'v': case 'V':
+            if (theGPT->Verify() > 0) { // problems found
+               printf("You may be able to correct the problems by using options on the experts\n"
+                     "menu (press 'x' at the command prompt). Good luck!\n");
+            } // if
+            break;
+         case 'w': case 'W':
+            if (theGPT->SaveGPTData() == 1)
+               goOn = 0;
+            break;
+         case 'x': case 'X':
+            ExpertsMenu(filename, theGPT);
+            goOn = 0;
+            break;
+         default:
+            ShowCommands();
+            break;
+      } // switch
+   } while (goOn);
+} // MainMenu()
 
 void ShowCommands(void) {
-   printf("b\tconvert BSD disklabel partitions\n");
+   printf("b\tback up GPT data to a file\n");
    printf("c\tchange a partition's name\n");
    printf("d\tdelete a partition\n");
    printf("i\tshow detailed information on a partition\n");
-   printf("l\tlist available partition types\n");
-   printf("m\tprint this menu\n");
+   printf("l\tlist known partition types\n");
    printf("n\tadd a new partition\n");
    printf("o\tcreate a new empty GUID partition table (GPT)\n");
    printf("p\tprint the partition table\n");
    printf("q\tquit without saving changes\n");
+   printf("r\trecovery and transformation options (experts only)\n");
    printf("s\tsort partitions\n");
    printf("t\tchange a partition's type code\n");
    printf("v\tverify disk\n");
    printf("w\twrite table to disk and exit\n");
    printf("x\textra functionality (experts only)\n");
+   printf("?\tprint this menu\n");
 } // ShowCommands()
 
-// Accept a command and execute it. Returns 0 if the command includes
-// an exit condition (such as a q or w command), 1 if more commands
-// should be processed.
-int ExpertsMenu(char* filename, struct GPTData* theGPT) {
+// Accept a recovery & transformation menu command. Returns only when the user
+// issues an exit command, such as 'w' or 'q'.
+void RecoveryMenu(char* filename, struct GPTData* theGPT) {
    char command, line[255], buFile[255];
-   int retval = 1;
+   PartTypes typeHelper;
+   uint32_t temp1;
+   int goOn = 1;
+
+   do {
+      printf("\nrecovery/transformation command (? for help): ");
+      fgets(line, 255, stdin);
+      sscanf(line, "%c", &command);
+      switch (command) {
+         case 'b': case 'B':
+            theGPT->RebuildMainHeader();
+            break;
+         case 'c': case 'C':
+            printf("Warning! This will probably do weird things if you've converted an MBR to\n"
+                  "GPT form and haven't yet saved the GPT! Proceed? ");
+            if (GetYN() == 'Y')
+               theGPT->LoadSecondTableAsMain();
+            break;
+         case 'd': case 'D':
+            theGPT->RebuildSecondHeader();
+            break;
+         case 'e': case 'E':
+            printf("Warning! This will probably do weird things if you've converted an MBR to\n"
+                  "GPT form and haven't yet saved the GPT! Proceed? ");
+            if (GetYN() == 'Y')
+               theGPT->LoadMainTable();
+            break;
+         case 'f': case 'F':
+            printf("Warning! This will destroy the currently defined partitions! Proceed? ");
+            if (GetYN() == 'Y') {
+               if (theGPT->LoadMBR(filename) == 1) { // successful load
+                  theGPT->XFormPartitions();
+               } else {
+                  printf("Problem loading MBR! GPT is untouched; regenerating protective MBR!\n");
+                  theGPT->MakeProtectiveMBR();
+               } // if/else
+            } // if
+            break;
+         case 'g': case 'G':
+            temp1 = theGPT->XFormToMBR();
+            if (temp1 > 0) {
+               printf("Converted %d partitions. Finalize and exit? ", temp1);
+               if (GetYN() == 'Y') {
+                  if (theGPT->DestroyGPT(0) > 0)
+                     goOn = 0;
+               } else {
+                  theGPT->MakeProtectiveMBR();
+                  printf("Note: New protective MBR created.\n");
+               } // if/else
+            } // if
+            break;
+         case 'h': case 'H':
+            theGPT->MakeHybrid();
+            break;
+         case 'i': case 'I':
+            theGPT->ShowDetails();
+            break;
+         case 'l': case 'L':
+            printf("Enter backup filename to load: ");
+            fgets(line, 255, stdin);
+            sscanf(line, "%s", &buFile);
+            theGPT->LoadGPTBackup(buFile);
+            break;
+         case 'm': case 'M':
+            MainMenu(filename, theGPT);
+            goOn = 0;
+            break;
+         case 'o': case 'O':
+            theGPT->DisplayMBRData();
+            break;
+         case 'p': case 'P':
+            theGPT->DisplayGPTData();
+            break;
+         case 'q': case 'Q':
+            goOn = 0;
+            break;
+         case 't': case 'T':
+            theGPT->XFormDisklabel();
+            break;
+         case 'v': case 'V':
+            theGPT->Verify();
+            break;
+         case 'w': case 'W':
+            if (theGPT->SaveGPTData() == 1) {
+               goOn = 0;
+            } // if
+            break;
+         case 'x': case 'X':
+            ExpertsMenu(filename, theGPT);
+            goOn = 0;
+            break;
+         default:
+            ShowRecoveryCommands();
+            break;
+      } // switch
+   } while (goOn);
+} // RecoveryMenu()
+
+void ShowRecoveryCommands(void) {
+   printf("b\tuse backup GPT header (rebuilding main)\n");
+   printf("c\tload backup partition table from disk (rebuilding main)\n");
+   printf("d\tuse main GPT header (rebuilding backup)\n");
+   printf("e\tload main partition table from disk (rebuilding backup)\n");
+   printf("f\tload MBR and build fresh GPT from it\n");
+   printf("g\tconvert GPT into MBR and exit\n");
+   printf("h\tmake hybrid MBR\n");
+   printf("i\tshow detailed information on a partition\n");
+   printf("l\tload partition data from a backup file\n");
+   printf("m\treturn to main menu\n");
+   printf("o\tprint protective MBR data\n");
+   printf("p\tprint the partition table\n");
+   printf("q\tquit without saving changes\n");
+   printf("t\ttransform BSD disklabel partition\n");
+   printf("v\tverify disk\n");
+   printf("w\twrite table to disk and exit\n");
+   printf("x\textra functionality (experts only)\n");
+   printf("?\tprint this menu\n");
+} // ShowRecoveryCommands()
+
+// Accept an experts' menu command. Returns only after the user
+// selects an exit command, such as 'w' or 'q'.
+void ExpertsMenu(char* filename, struct GPTData* theGPT) {
+   char command, line[255];
    PartTypes typeHelper;
    uint32_t pn;
    uint32_t temp1, temp2;
    int goOn = 1;
 
    do {
-      printf("\nExpert command (m for help): ");
+      printf("\nExpert command (? for help): ");
       fgets(line, 255, stdin);
       sscanf(line, "%c", &command);
       switch (command) {
@@ -170,25 +302,7 @@
            else
                printf("No partitions\n");
             break;
-         case 'b': case 'B':
-            theGPT->RebuildMainHeader();
-            break;
          case 'c': case 'C':
-            printf("Warning! This will probably do weird things if you've converted an MBR to\n"
-                   "GPT form and haven't yet saved the GPT! Proceed? ");
-            if (GetYN() == 'Y')
-               theGPT->LoadSecondTableAsMain();
-            break;
-         case 'd': case 'D':
-            theGPT->RebuildSecondHeader();
-            break;
-         case 'e': case 'E':
-            printf("Warning! This will probably do weird things if you've converted an MBR to\n"
-                   "GPT form and haven't yet saved the GPT! Proceed? ");
-            if (GetYN() == 'Y')
-               theGPT->LoadMainTable();
-            break;
-         case 'f': case 'F':
             if (theGPT->GetPartRange(&temp1, &temp2) > 0) {
                pn = theGPT->GetPartNum();
                printf("Enter the partition's new unique GUID:\n");
@@ -199,23 +313,12 @@
             printf("Enter the disk's unique GUID:\n");
             theGPT->SetDiskGUID(GetGUID());
             break;
-         case 'h': case 'H':
-            theGPT->MakeHybrid();
-            break;
          case 'i': case 'I':
             theGPT->ShowDetails();
             break;
-         case 'k': case 'K':
-            printf("Enter backup filename to save: ");
-            fgets(line, 255, stdin);
-            sscanf(line, "%s", &buFile);
-	    theGPT->SaveGPTBackup(buFile);
-            break;
-         case 'l': case 'L':
-            printf("Enter backup filename to load: ");
-            fgets(line, 255, stdin);
-            sscanf(line, "%s", &buFile);
-	    theGPT->LoadGPTBackup(buFile);
+         case 'm': case 'M':
+            MainMenu(filename, theGPT);
+            goOn = 0;
             break;
          case 'n': case 'N':
             theGPT->MakeProtectiveMBR();
@@ -227,10 +330,10 @@
             theGPT->DisplayGPTData();
 	    break;
          case 'q': case 'Q':
-            retval = 0;
 	    goOn = 0;
 	    break;
          case 'r': case 'R':
+            RecoveryMenu(filename, theGPT);
             goOn = 0;
             break;
          case 's': case 'S':
@@ -241,14 +344,12 @@
             break;
          case 'w': case 'W':
             if (theGPT->SaveGPTData() == 1) {
-               retval = 0;
                goOn = 0;
             } // if
             break;
          case 'z': case 'Z':
             if (theGPT->DestroyGPT() == 1) {
-               retval = 0;
-	       goOn = 0;
+               goOn = 0;
             }
             break;
          default:
@@ -256,29 +357,22 @@
             break;
       } // switch
    } while (goOn);
-   return (retval);
 } // ExpertsMenu()
 
 void ShowExpertCommands(void) {
    printf("a\tset attributes\n");
-   printf("b\tuse backup GPT header (rebuilding main)\n");
-   printf("c\tload backup partition table from disk (rebuilding main)\n");
-   printf("d\tuse main GPT header (rebuilding backup)\n");
-   printf("e\tload main partition table from disk (rebuilding backup)\n");
-   printf("f\tchange partition GUID\n");
+   printf("c\tchange partition GUID\n");
    printf("g\tchange disk GUID\n");
-   printf("h\tmake hybrid MBR\n");
    printf("i\tshow detailed information on a partition\n");
-   printf("k\tsave partition data to a backup file\n");
-   printf("l\tload partition data from a backup file\n");
-   printf("m\tprint this menu\n");
+   printf("m\treturn to main menu\n");
    printf("n\tcreate a new protective MBR\n");
    printf("o\tprint protective MBR data\n");
    printf("p\tprint the partition table\n");
    printf("q\tquit without saving changes\n");
-   printf("r\treturn to main menu\n");
+   printf("r\trecovery and transformation options (experts only)\n");
    printf("s\tresize partition table\n");
    printf("v\tverify disk\n");
    printf("w\twrite table to disk and exit\n");
-   printf("z\tDestroy GPT data structures and exit\n");
+   printf("z\tzap (destroy) GPT data structures and exit\n");
+   printf("?\tprint this menu\n");
 } // ShowExpertCommands()