Added files for sgdisk program
diff --git a/sgdisk.8 b/sgdisk.8
new file mode 100644
index 0000000..48a740d
--- /dev/null
+++ b/sgdisk.8
@@ -0,0 +1,467 @@
+.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" May be distributed under the GNU General Public License
+.TH "SGDISK" "8" "0.6.0" "Roderick W. Smith" "GPT fdisk Manual"
+.SH "NAME"
+sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix
+.SH "SYNOPSIS"
+.BI "sgdisk "
+[ options ]
+.I device
+
+.SH "DESCRIPTION"
+GPT fdisk is a text\-mode menu\-driven package for creation and manipulation of
+partition tables. It consists of two programs: the text\-mode interactive
+\fBgdisk\fR and the command\-line \fBsgdisk\fR. Either program will
+automatically convert an old\-style Master Boot Record (MBR) partition table
+or BSD disklabel stored without an MBR carrier partition to the newer Globally
+Unique Identifier (GUID) Partition Table (GPT) format, or will load a GUID
+partition table. This man page documents the command\-line \fBsgdisk\fR
+program.
+
+Some advanced data manipulation and
+recovery options require you to understand the distinctions between the
+main and backup data, as well as between the GPT headers and the partition
+tables. For information on MBR vs. GPT, as well as GPT terminology and
+structure, see the extended \fBgdisk\fR documentation at
+\fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia.
+
+The \fBsgdisk\fR program employs a user interface that's based entirely on
+the command line, making it suitable for use in scripts or by experts who
+want to make one or two quick changes to a disk. (The program may query the
+user when certain errors are encountered, though.) The program's name is
+based on \fBsfdisk\fR, but the user options of the two programs are
+entirely different from one another.
+
+Ordinarily, \fBsgdisk\fR operates on disk device files, such as
+\fI/dev/sda\fR or \fI/dev/hda\fR under Linux, \fI/dev/disk0\fR under
+Mac OS X, or \fI/dev/ad0\fR or \fI/dev/da0\fR under FreeBSD. The program
+can also operate on disk image files, which can be either copies of whole
+disks (made with \fBdd\fR, for instance) or raw disk images used by
+emulators such as QEMU or VMWare. Note that only \fIraw\fR disk images
+are supported; \fBsgdisk\fR cannot work on compressed or other advanced
+disk image formats.
+
+The MBR partitioning system uses a combination of cylinder/head/sector
+(CHS) addressing and logical block addressing (LBA). The former is klunky
+and limiting. GPT drops CHS addressing and uses 64\-bit LBA mode
+exclusively. Thus, GPT data structures, and therefore
+\fBsgdisk\fR, do not need to deal with CHS geometries and all the problems
+they create.
+
+For best results, you should use an OS\-specific partition table
+program whenever possible. For example, you should make Mac OS X
+partitions with the Mac OS X Disk Utility program and Linux partitions
+with the Linux \fBgdisk\fR, \fBsgdisk\fR, or GNU Parted programs.
+
+Upon start, \fBsgdisk\fR attempts to identify the partition type in use on
+the disk. If it finds valid GPT data, \fBsgdisk\fR will use it. If
+\fBsgdisk\fR finds a valid MBR or BSD disklabel but no GPT data, it will
+attempt to convert the MBR or disklabel into GPT form. (BSD disklabels are
+likely to have unusable first and/or final partitions because they overlap
+with the GPT data structures, though.) GPT fdisk can identify, but not use
+data in, Apple Partition Map (APM) disks, which are used on 680x0\- and
+PowerPC\-based Macintoshes. If you specify any option that results in changes
+to an MBR or BSD disklabel, \fBsgdisk\fR ignores those changes unless the
+\fI\-g\fR (\fI\-\-mbrtogpt\fR) or \fI\-z\fR (\fI\-\-zap\fR) option is used.
+If you use the \fI\-g\fR option, \fBsgdisk\fR replaces the MBR or disklabel
+with a GPT. \fIThis action is potentially dangerous!\fR Your system may become
+unbootable, and partition type codes may become corrupted if the disk uses
+unrecognized type codes. Boot problems are particularly likely if you're
+multi\-booting with any GPT\-unaware OS.
+
+The MBR\-to\-GPT conversion will leave at least one gap in the partition
+numbering if the original MBR used logical partitions. These gaps are
+harmless, but you can eliminate them by using the \fI\-s\fR (\fI\-\-sort\fR)
+option, if you like. (Doing this may require you to update your
+\fI/etc/fstab\fR file.)
+
+When creating a fresh partition table, certain considerations may be in
+order:
+
+.TP 
+.B *
+For data (non\-boot) disks, and for boot disks used on BIOS\-based computers
+with GRUB as the boot loader, partitions may be created in whatever order
+and in whatever sizes are desired.
+
+.TP 
+.B *
+Boot disks for EFI\-based systems require an \fIEFI System
+Partition\fR (\fBsgdisk\fR internal code 0xEF00) formatted as FAT\-32.
+The recommended size of this partition is between 100 and 200 MiB.
+Boot\-related files are stored here. (Note that GNU Parted identifies
+such partitions as having the "boot flag" set.)
+
+.TP 
+.B *
+Some boot loaders for BIOS\-based systems make use of a \fIBIOS Boot
+Partition\fR (\fBsgdisk\fR internal code 0xEF02), in which the secondary
+boot loader is stored, possibly without the benefit of a filesystem. This
+partition can typically be quite small (roughly 32 to 200 KiB), but you
+should consult your boot loader documentation for details.
+
+.TP 
+.B *
+If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft
+Reserved\fR (\fBsgdisk\fR
+internal code 0x0C01) is recommended. This partition should be about 128 MiB
+in size. It ordinarily follows the EFI System Partition and immediately
+precedes the Windows data partitions. (Note that GNU Parted creates all
+FAT partitions as this type, which actually makes the partition unusable
+for normal file storage in both Windows and Mac OS X.)
+
+.TP 
+.B *
+Some OSes' GPT utilities create some blank space (typically 128 MiB) after
+each partition. The intent is to enable future disk utilities to use this
+space. Such free space is not required of GPT disks, but creating it may
+help in future disk maintenance.
+
+.SH "OPTIONS"
+Some options take no arguments, others take one argument (typically a partition
+number), and others take compound arguments with colon delimitation. For
+instance, \fI\-n\fR (\fI\-\-new\fR) takes a partition number, a starting
+sector number, and an ending sector number, as in \fBsgdisk \-n 2:2000:50000
+/dev/sdc\fR, which creates a new partition, numbered 2, starting at sector
+2000 an ending at sector 50,000, on \fI/dev/sdc\fR.
+
+Unrelated options may be combined; however, some such combinations will be
+nonsense (such as deleting a partition and then changing its GUID type code).
+\fBsgdisk\fR interprets options in the order in which they're entered, so
+effects can vary depending on order. For instance, \fBsgdisk \-s \-d 2\fR
+sorts the partition table entries and then deletes partition 2 from the
+newly\-sorted list; but \fBsgdisk \-d 2 \-s\fR deletes the original partition
+2 and then sorts the modified partition table.
+
+Error checking and opportunities to correct mistakes in \fBsgdisk\fR are
+minimal. Although the program endeavors to keep the GPT data structures legal,
+it does not prompt for verification before performing its actions. Unless you
+require a command\-line\-driven program, you should use the interactive
+\fBgdisk\fR instead of \fBsgdisk\fR, since \fBgdisk\fR allows you to
+quit without saving your changes, should you make a mistake.
+
+Although \fBsgdisk\fR is based on the same partition\-manipulation code as
+\fBgdisk\fR, \fBsgdisk\fR implements fewer features than its interactive
+sibling. Options available in \fBsgdisk\fR are:
+
+.TP 
+.B \-a, \-\-set\-alignment=value
+Set the sector alignment multiple. GPT fdisk aligns the start of partitions
+to sectors that are multiples of this value, which defaults to 8 on disks
+larger than 800GiB with 512\-byte sectors and to 1 on smaller disks or those
+with non\-512\-byte sectors. This alignment value is necessary to obtain
+optimum performance with Western Digital Advanced Format and similar drives
+with larger physical than logical sector sizes.
+
+.TP 
+.B \-b, \-\-backup=file
+Save partition data to a backup file. You can back up your current
+in\-memory partition table to a disk file using this option. The resulting
+file is a binary file consisting of the protective MBR, the main GPT
+header, the backup GPT header, and one copy of the partition table, in that
+order. Note that the backup is of the current in\-memory data structures, so
+if you launch the program, make changes, and then use this option, the
+backup will reflect your changes. If the GPT data structures are damaged,
+the backup may not accurately reflect the damaged state; instead, they
+will reflect GPT fdisk's first\-pass interpretation of the GPT.
+
+.TP 
+.B \-c, \-\-change=partnum:name
+Change the GPT name of a partition. This name is encoded as a UTF\-16
+string, but \fBsgdisk\fR
+supports only ASCII characters as names. For the most part, Linux ignores
+the partition name, but it may be important in some OSes. GPT fdisk sets
+a default name based on the partition type code. If you want to set a name
+that includes a space, enclose it in quotation marks, as in
+\fIsgdisk \-c 1:"Sample Name" /dev/sdb\fR.
+
+.TP 
+.B \-d, \-\-delete=partnum
+Delete a partition. This action deletes the entry from the partition table
+but does not disturb the data within the sectors originally allocated to
+the partition on the disk. If a corresponding hybrid MBR partition exists,
+\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT)
+MBR protective partition to fill the new free space.
+
+.TP 
+.B e, \-\-move\-second\-header
+Move backup GPT data structures to the end of the disk. Use this option if
+you've added disks to a RAID array, thus creating a virtual disk with space
+that follows the backup GPT data structures. This command moves the backup
+GPT data structures to the end of the disk, where they belong.
+
+.TP 
+.B E, \-\-end\-of\-largest
+Displays the sector number of the end of the largest available block of
+sectors on the disk. A script may store this value and pass it back as
+part of \fI\-n\fR's option to create a partition. If no unallocated
+sectors are available, this function returns the value 0.
+
+.TP 
+.B \-f, \-\-first\-in\-largest
+Displays the sector number of the start of the largest available block of
+sectors on the disk. A script may store this value and pass it back as
+part of \fI\-n\fR's option to create a partition. If no unallocated
+sectors are available, this function returns the value 0.
+
+.TP 
+.B \-g, \-\-mbrtogpt
+Convert an MBR disk to a GPT disk. As a safety measure, use of this option
+is required on MBR or BSD disklabel disks if you intend to save your changes,
+in order to prevent accidentally damaging such disks.
+
+.TP 
+.B \-i, \-\-info=partnum
+Show detailed partition information. The summary information produced by
+the \fI\-p\fR command necessarily omits many details, such as the partition's
+unique GUID and the translation of \fBsgdisk\fR's
+internal partition type code to a plain type name. The \fI\-i\fR option
+displays this information for a single partition.
+
+.TP 
+.B \-l, \-\-load\-backup=file
+Load partition data from a backup file. This option is the reverse of the
+\fI\-b\fR option. Note that restoring partition data from anything
+but the original disk is not recommended.
+
+.TP 
+.B \-L, \-\-list\-types
+Display a summary of partition types. GPT uses a GUID to identify
+partition types for particular OSes and purposes. For ease of data entry,
+\fBsgdisk\fR compresses these into two\-byte (four\-digit hexadecimal)
+values that are related to their equivalent MBR codes. Specifically, the
+MBR code is multiplied by hexadecimal 0x0100. For instance, the code for
+Linux swap space in MBR is 0x82, and it's 0x8200 in \fBgdisk\fR.
+A one\-to\-one correspondence is impossible, though. Most notably, many DOS,
+Windows, and Linux data partition codes correspond to a single GPT code
+(entered as 0x0700 in \fBsgdisk\fR). Some OSes use a single MBR code but
+employ many more codes in GPT. For these, \fBsgdisk\fR
+adds code numbers sequentially, such as 0xa500 for a FreeBSD disklabel,
+0xa501 for FreeBSD boot, 0xa502 for FreeBSD swap, and so on. Note that
+these two\-byte codes are unique to \fBgdisk\fR and \fBsgdisk\fR. This
+option does not require you to specify a valid disk device filename.
+
+.TP 
+.B \-n, \-\-new=partnum:start:end
+Create a new partition. You enter a partition
+number, starting sector, and an ending sector. Both start and end sectors
+can be specified in absolute terms as sector numbers or as positions
+measured in kilobytes (K), megabytes (M), gigabytes (G), or terabytes (T);
+for instance, \fI\fB40M\fR\fR specifies a position 40MiB from the start of
+the disk. You can specify locations relative to the start or end of the
+specified default range by preceding the number by a '+' or '\-' symbol, as
+in \fI\fB+2G\fR\fR to specify a point 2GiB after the default start sector,
+or \fI\fB\-200M\fR\fR to specify a point 200MiB before the last available
+sector. Pressing the Enter key with no input specifies the default value,
+which is the start of the largest available block for the start sector and
+the end of the same block for the end sector.
+
+.TP 
+.B \-o, \-\-clear
+Clear out all partition data. This includes GPT header data,
+all partition definitions, and the protective MBR.
+
+.TP 
+.B \-p, \-\-print
+Display basic partition summary data. This includes partition
+numbers, starting and ending sector numbers, partition sizes,
+\fBsgdisk\fR's partition types codes, and partition names. For
+additional information, use the \fI\-i\fR (\fI\-\-info\fR) option.
+
+.TP 
+.B \-P, \-\-pretend
+Pretend to make specified changes. In\-memory GPT data structures are
+altered according to other parameters, but changes are not written
+to disk.
+
+.TP 
+.B \-s, \-\-sort
+Sort partition entries. GPT partition numbers need not match the order of
+partitions on the disk. If you want them to match, you can use this option.
+Note that some partitioning utilities sort partitions whenever they make
+changes. Such changes will be reflected in your device filenames, so you
+may need to edit \fI/etc/fstab\fR if you use this option.
+
+.TP 
+.B \-t, \-\-typecode=partnum:hexcode
+Change a single partition's type code. You enter the type code using a
+two\-byte hexadecimal number, as described earlier.
+
+.TP 
+.B \-T, \-\-transform\-bsd=partnum
+Transform BSD partitions into GPT partitions. This option works on BSD
+disklabels held within GPT (or converted MBR) partitions. Converted
+partitions' type codes are likely to need manual adjustment. \fBsgdisk\fR
+will attempt to convert BSD disklabels stored on the main disk when
+launched, but this conversion is likely to produce first and/or last
+partitions that are unusable. The many BSD variants means that the
+probability of \fBsgdisk\fR being unable to convert a BSD disklabel is
+high compared to the likelihood of problems with an MBR conversion.
+
+.TP 
+.B \-\-usage
+Print a brief summary of available options.
+
+.TP 
+.B \-v, \-\-verify
+Verify disk. This option checks for a variety of problems, such as
+incorrect CRCs and mismatched main and backup data. This option does not
+automatically correct most problems, though; for that, you must use
+options on the recovery & transformation menu. If no problems are found,
+this command displays a summary of unallocated disk space.
+
+.TP 
+.B \-V, \-\-version
+Display program version information. This option may be used without
+specifying a device filename.
+
+.TP 
+.B \-z, \-\-zap
+Zap (destroy) the GPT data structures and exit. Use this option if you want to
+repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program.
+You'll be given the choice of preserving the existing MBR, in case it's a
+hybrid MBR with salvageable partitions or if you've already created new MBR
+partitions and want to erase the remnants of your GPT partitions. \fIIf you've
+already created new MBR partitions, it's conceivable that this option will
+damage the first and/or last MBR partitions!\fR Such an event is unlikely, but
+could occur if your new MBR partitions overlap the old GPT data structures.
+
+.TP 
+.B \-?, \-\-help
+Print a summary of options.
+
+.SH "RETURN VALUES"
+\fBsgdisk\fR returns various values depending on its success or failure:
+
+.TP 
+.B 0
+Normal program execution
+
+.TP 
+.B 1
+Too few arguments
+
+.TP 
+.B 4
+An error occurred while reading the partition table
+
+.TP 
+.B 3
+Non\-GPT disk detected and no \fI\-g\fR option
+
+.TP 
+.B 4
+An error prevented saving changes
+.SH "BUGS"
+As of January 2010 (version 0.6.0), \fBsgdisk\fR
+should be considered beta software. Known bugs and limitations include:
+
+.TP 
+.B *
+The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Linux
+versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been
+tested, with the x86\-64 version having seen the most testing.
+
+.TP 
+.B *
+The FreeBSD version of the program can't write changes to the partition
+table to a disk when existing partitions on that disk are mounted. (The
+same problem exists with many other FreeBSD utilities, such as
+\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.)
+
+.TP 
+.B *
+The fields used to display the start and end sector numbers for partitions
+in the \fI\-p\fR option are 14 characters wide. This translates to a limitation
+of about 45 PiB. On larger disks, the displayed columns will go out of
+alignment.
+
+.TP 
+.B *
+Only ASCII characters are supported in the partition name field. If an
+existing partition uses non\-ASCII UTF\-16 characters, they're likely to be
+corrupted in the 'i' and 'p' menu options' displays; however, they should be
+preserved when loading and saving partitions.
+
+.TP 
+.B *
+The program can load only up to 128 partitions (4 primary partitions and
+124 logical partitions) when converting from MBR format. This limit can
+be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the
+\fImbr.h\fR source code file and recompiling; however, such a change
+will require using a larger\-than\-normal partition table. (The limit
+of 128 partitions was chosen because that number equals the 128 partitions
+supported by the most common partition table size.)
+
+.TP 
+.B *
+Converting from MBR format sometimes fails because of insufficient space at
+the start or (more commonly) the end of the disk. Resizing the partition
+table (using the 's' option in the experts' menu) can sometimes overcome
+this problem; however, in extreme cases it may be necessary to resize a
+partition using GNU Parted or a similar tool prior to conversion with
+\fBgdisk\fR.
+
+.TP 
+.B *
+MBR conversions work only if the disk has correct LBA partition
+descriptors. These descriptors should be present on any disk over 8 GiB in
+size or on smaller disks partitioned with any but very ancient software.
+
+.TP 
+.B *
+BSD disklabel support can create first and/or last partitions that overlap
+with the GPT data structures. This can sometimes be compensated by
+adjusting the partition table size, but in extreme cases the affected
+partition(s) may need to be deleted.
+
+.TP 
+.B *
+Because of the highly variable nature of BSD disklabel structures,
+conversions from this form may be unreliable \-\- partitions may be dropped,
+converted in a way that creates overlaps with other partitions, or
+converted with incorrect start or end values. Use this feature with
+caution!
+
+.TP 
+.B *
+Booting after converting an MBR or BSD disklabel disk is likely to be
+disrupted. Sometimes re\-installing a boot loader will fix the problem, but
+other times you may need to switch boot loaders. Except on EFI\-based
+platforms, Windows through at least Windows 7 RC doesn't support booting
+from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery &
+transformation menu) or abandoning GPT in favor of MBR may be your only
+options in this case.
+
+.PP 
+
+The support for big\-endian CPUs (PowerPC, for example) is new, as of version
+0.3.5. I advise using caution on that platform, particularly with the more
+obscure features of the program.
+
+.SH "AUTHORS"
+Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
+
+Contributors:
+
+* Yves Blusseau (1otnwmz02@sneakemail.com)
+
+* David Hubbard (david.c.hubbard@gmail.com)
+
+.SH "SEE ALSO"
+\fBcfdisk (8)\fR,
+\fBfdisk (8)\fR,
+\fBgdisk (8)\fR,
+\fBmkfs (8)\fR,
+\fBparted (8)\fR,
+\fBsfdisk (8)\fR
+
+\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
+
+\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR
+
+\fIhttp://www.rodsbooks.com/gdisk/\fR
+
+.SH "AVAILABILITY"
+The \fBsgdisk\fR command is part of the \fIGPT fdisk\fR package and is
+available from Rod Smith.
diff --git a/sgdisk.cc b/sgdisk.cc
index 9479afb..027e37f 100644
--- a/sgdisk.cc
+++ b/sgdisk.cc
@@ -1,464 +1,290 @@
 // sgdisk.cc
-// Program modelled after Linux sfdisk, but it manipulates GPT partitions
-// rather than MBR partitions. This is effectively a new user interface
-// to my gdisk program.
+// Command-line-based version of gdisk. This program is named after sfdisk,
+// and it can serve a similar role (easily scripted, etc.), but it's used
+// strictly via command-line arguments, and it doesn't bear much resemblance
+// to sfdisk in actual use.
 //
-// by Rod Smith, project began February 2009
+// by Rod Smith, project began February 2009; sgdisk begun January 2010.
 
 /* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed
   under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
 
-//#include <iostream>
 #include <stdio.h>
-#include <string.h>
-#include <getopt.h>
+#include <string>
+#include <popt.h>
+#include <errno.h>
 #include "mbr.h"
 #include "gpt.h"
 #include "support.h"
+#include "parttypes.h"
+
+using namespace std;
 
 #define MAX_OPTIONS 50
 
-// Function prototypes....
-/* void MainMenu(char* filename, struct GPTData* theGPT);
-void ShowCommands(void);
-void ExpertsMenu(char* filename, struct GPTData* theGPT);
-void ShowExpertCommands(void);
-void RecoveryMenu(char* filename, struct GPTData* theGPT);
-void ShowRecoveryCommands(void); */
+uint64_t GetInt(char* Info, int itemNum);
+string GetString(char* Info, int itemNum);
 
-enum Commands { NONE, LIST, VERIFY };
-
-struct Options {
-   Commands theCommand;
-   char* theArgument;
-}; // struct Options
-
-int verbose_flag;
-
-static struct option long_options[] =
-{
-   {"verify",  no_argument, NULL, 'v'},
-   {"list",    no_argument, NULL, 'l'},
-   {0, 0, NULL, 0}
-};
-
-int ParseOptions(int argc, char* argv[], Options* theOptions, char** device);
-
-int main(int argc, char* argv[]) {
+int main(int argc, char *argv[]) {
    GPTData theGPT;
-   int doMore = 1, opt, i, numOptions = 0;
+   int opt, i, numOptions = 0, saveData = 0, neverSaveData = 0;
+   int partNum = 0, deletePartNum = 0, infoPartNum = 0, bsdPartNum = 0, saveNonGPT = 1;
+   int alignment = 8, retval = 0, pretend = 0;
+   uint16_t hexCode;
+   uint32_t tableSize = 128;
+   uint64_t startSector, endSector;
    char* device = NULL;
-   Options theOptions[MAX_OPTIONS];
-
-   printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n");
-   numOptions = ParseOptions(argc, argv, theOptions, &device);
-
-   if (device != NULL) {
-      if (theGPT.LoadPartitions(device)) {
-         for (i = 0; i < numOptions; i++) {
-            switch (theOptions[i].theCommand) {
-               case LIST:
-                  theGPT.JustLooking();
-                  theGPT.DisplayGPTData();
-                  break;
-               case VERIFY:
-                  theGPT.JustLooking();
-                  theGPT.Verify();
-                  break;
-               case NONE:
-                  printf("Usage: %s {-lv} device\n", argv[0]);
-                  break;
-            } // switch
-         } // for
-      } // if loaded OK
-   } // if (device != NULL)
-
-   return 0;
-} // main
-
-// Parse command-line options. Returns the number of arguments retrieved
-int ParseOptions(int argc, char* argv[], Options* theOptions, char** device) {
-   int opt, i, numOptions = 0;
-   int verbose_flag;
-
-   // Use getopt() to extract commands and their arguments
-   /* getopt_long stores the option index here. */
-   int option_index = 0;
-
-//   c = getopt_long (argc, argv, "abc:d:f:",
-//                    long_options, &option_index);
-
-   while (((opt = getopt_long(argc, argv, "vl", long_options, &option_index)) != -1)
-          && (numOptions < MAX_OPTIONS)) {
-      printf("opt is %c, option_index is %d\n", opt, option_index);
-      switch (opt) {
-         case 'l':
-            printf("Entering list option, numOptions = %d!\n", numOptions);
-            theOptions[numOptions].theCommand = LIST;
-            theOptions[numOptions++].theArgument = NULL;
-            break;
-         case 'v':
-            theOptions[numOptions].theCommand = VERIFY;
-            theOptions[numOptions++].theArgument = NULL;
-            break;
-         default:
-            printf("Default switch; opt is %c\n", opt);
-            break;
-//            abort();
-      } // switch
-   } // while
-
-   // Find non-option arguments. If the user types a legal command, there
-   // will be only one of these: The device filename....
-   opt = 0;
-   printf("Searching for device filename; optind is %d\n", optind);
-   for (i = optind; i < argc; i++) {
-      *device = argv[i];
-      printf("Setting device to %s\n", argv[i]);
-      opt++;
-   } // for
-   if (opt > 1) {
-      fprintf(stderr, "Warning! Found stray unrecognized arguments! Program may misbehave!\n");
-   } // if
-
-   return numOptions;
-} // ParseOptions()
-
-/* // 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];
-   char* junk;
-   int goOn = 1;
+   char *argument = NULL, *newPartInfo = NULL, *typeCode = NULL, *partName;
+   char *backupFile = NULL;
    PartTypes typeHelper;
-   uint32_t temp1, temp2;
 
-   do {
-      printf("\nCommand (? for help): ");
-      junk = fgets(line, 255, stdin);
-      sscanf(line, "%c", &command);
-      switch (command) {
-         case 'b': case 'B':
-            printf("Enter backup filename to save: ");
-            junk = fgets(line, 255, stdin);
-            sscanf(line, "%s", (char*) &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':
+   poptContext poptCon;
+   struct poptOption theOptions[] =
+   {
+      {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"},
+      {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"},
+      {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"},
+      {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"},
+      {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""},
+      {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""},
+      {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""},
+      {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""},
+      {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
+      {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
+      {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
+      {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"},
+      {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""},
+      {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""},
+      {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""},
+      {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""},
+      {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"},
+      {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:hexcode"},
+      {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"},
+      {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""},
+      {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""},
+      {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT data structures", ""},
+      POPT_AUTOHELP { NULL, 0, 0, NULL, 0 }
+   };
+
+   // Create popt context...
+   poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0);
+
+   poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>");
+
+   if (argc < 2) {
+      poptPrintUsage(poptCon, stderr, 0);
+      exit(1);
+   }
+
+   // Do one loop through the options to find the device filename and deal
+   // with options that don't require a device filename....
+   while ((opt = poptGetNextOpt(poptCon)) > 0) {
+      switch (opt) {
+         case 'L':
             typeHelper.ShowTypes();
             break;
-         case 'n': case 'N':
-            theGPT->CreatePartition();
+         case 'P':
+            pretend = 1;
             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;
+         case 'V':
+            printf("GPT fdisk (sgdisk) version 0.5.4-pre1\n\n");
             break;
          default:
-            ShowCommands();
             break;
       } // switch
-   } while (goOn);
-} // MainMenu()
+      numOptions++;
+   } // while
 
-void ShowCommands(void) {
-   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 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()
+   // Assume first non-option argument is the device filename....
+   device = (char*) poptGetArg(poptCon);
+   poptResetContext(poptCon);
 
-// 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];
-   char* junk;
-   PartTypes typeHelper;
-   uint32_t temp1;
-   int goOn = 1;
+   if (device != NULL) {
+      theGPT.JustLooking(); // reset as necessary
+      theGPT.BeQuiet(); // Tell called functions to be less verbose & interactive
+      if (theGPT.LoadPartitions(device)) {
+         if ((theGPT.WhichWasUsed() == use_mbr) || (theGPT.WhichWasUsed() == use_bsd))
+            saveNonGPT = 0; // flag so we don't overwrite unless directed to do so
+         while ((opt = poptGetNextOpt(poptCon)) > 0) {
+            switch (opt) {
+               case 'a':
+                  theGPT.SetAlignment(alignment);
+                  break;
+               case 'b':
+                  theGPT.SaveGPTBackup(backupFile);
+                  free(backupFile);
+                  break;
+               case 'c':
+                  theGPT.JustLooking(0);
+                  partNum = (int) GetInt(partName, 1) - 1;
+                  if (theGPT.SetName(partNum, (char*) GetString(partName, 2).c_str())) {
+                     saveData = 1;
+                  } else {
+                     fprintf(stderr, "Unable set set partition %d's name to '%s'!\n",
+                             partNum + 1, GetString(partName, 2).c_str());
+                     neverSaveData = 1;
+                  } // if/else
+                  free(partName);
+                  break;
+               case 'd':
+                  theGPT.JustLooking(0);
+                  if (theGPT.DeletePartition(deletePartNum - 1) == 0) {
+                     fprintf(stderr, "Error %d deleting partition!\n", errno);
+                     neverSaveData = 1;
+                  } else saveData = 1;
+                  break;
+               case 'e':
+                  theGPT.JustLooking(0);
+                  theGPT.MoveSecondHeaderToEnd();
+                  saveData = 1;
+                  break;
+               case 'E':
+                  printf("%llu\n", (unsigned long long) theGPT.FindLastAvailable(theGPT.FindFirstInLargest()));
+                  break;
+               case 'f':
+                  printf("%llu\n", (unsigned long long) theGPT.FindFirstInLargest());
+                  break;
+               case 'g':
+                  theGPT.JustLooking(0);
+                  saveData = 1;
+                  saveNonGPT = 1;
+                  break;
+               case 'i':
+                  theGPT.ShowPartDetails(infoPartNum - 1);
+                  break;
+               case 'l':
+                  if (theGPT.LoadGPTBackup(backupFile) == 1)
+                     saveData = 1;
+                  else {
+                     saveData = 0;
+                     neverSaveData = 1;
+                     fprintf(stderr, "Error loading backup file!\n");
+                  } // else
+                  free(backupFile);
+                  break;
+               case 'L':
+                  break;
+               case 'n':
+                  theGPT.JustLooking(0);
+                  partNum = (int) GetInt(newPartInfo, 1) - 1;
+                  startSector = GetInt(newPartInfo, 2);
+                  endSector = GetInt(newPartInfo, 3);
+                  if (theGPT.CreatePartition(partNum, startSector, endSector)) {
+                     saveData = 1;
+                  } else {
+                     fprintf(stderr, "Could not create partition %d from %llu to %llu!\n",
+                             partNum, startSector, endSector);
+                     neverSaveData = 1;
+                  } // if/else
+                  free(newPartInfo);
+                  break;
+               case 'o':
+                  theGPT.JustLooking(0);
+                  theGPT.ClearGPTData();
+                  saveData = 1;
+                  break;
+               case 'p':
+                  theGPT.DisplayGPTData();
+                  break;
+               case 'P':
+                  pretend = 1;
+                  break;
+               case 's':
+                  theGPT.JustLooking(0);
+                  theGPT.SortGPT();
+                  saveData = 1;
+                  break;
+               case 'S':
+                  theGPT.JustLooking(0);
+                  if (theGPT.SetGPTSize(tableSize) == 0)
+                     neverSaveData = 1;
+                  else
+                     saveData = 1;
+                  break;
+               case 't':
+                  theGPT.JustLooking(0);
+                  partNum = (int) GetInt(typeCode, 1) - 1;
+                  sscanf(GetString(typeCode, 2).c_str(), "%x", &hexCode);
+                  if (theGPT.ChangePartType(partNum, hexCode)) {
+                     saveData = 1;
+                  } else {
+                     fprintf(stderr, "Could not change partition %d's type code to %x!\n",
+                             partNum + 1, hexCode);
+                     neverSaveData = 1;
+                  } // if/else
+                  free(typeCode);
+                  break;
+               case 'T':
+                  theGPT.JustLooking(0);
+                  theGPT.XFormDisklabel(bsdPartNum);
+                  saveData = 1;
+                  break;
+               case 'v':
+                  theGPT.Verify();
+                  break;
+               case 'z':
+                  if (!pretend)
+                     theGPT.DestroyGPT(-1);
+                  saveNonGPT = 0;
+                  break;
+               default:
+                  printf("Unknown option (-%c)!\n", opt);
+                  break;
+            } // switch
+         } // while
+         if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend))
+            theGPT.SaveGPTData(1);
+         if (saveData && (!saveNonGPT)) {
+            printf("Non-GPT disk; not saving changes. Use -g to override.\n");
+            retval = 3;
+         } // if
+         if (neverSaveData) {
+            printf("Error encountered; not saving changes.\n");
+            retval = 4;
+         } // if
+      } else { // if loaded OK
+         retval = 2;
+      } // if/else loaded OK
+   } // if (device != NULL)
+   poptFreeContext(poptCon);
 
-   do {
-      printf("\nrecovery/transformation command (? for help): ");
-      junk = 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: ");
-            junk = fgets(line, 255, stdin);
-            sscanf(line, "%s", (char*) &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()
+   return retval;
+} // main
 
-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()
+// Extract integer data from argument string, which should be colon-delimited
+uint64_t GetInt(char* argument, int itemNum) {
+   int startPos = -1, endPos = -1;
+   uint64_t retval = 0;
+   string Info;
 
-// 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];
-   char* junk;
-   PartTypes typeHelper;
-   uint32_t pn;
-   uint32_t temp1, temp2;
-   int goOn = 1;
+   Info = argument;
+   while (itemNum-- > 0) {
+      startPos = endPos + 1;
+      endPos = Info.find(':', startPos);
+   }
+   if (endPos == string::npos)
+      endPos = Info.length();
+   endPos--;
 
-   do {
-      printf("\nExpert command (? for help): ");
-      junk = fgets(line, 255, stdin);
-      sscanf(line, "%c", &command);
-      switch (command) {
-         case 'a': case 'A':
-            if (theGPT->GetPartRange(&temp1, &temp2) > 0)
-               theGPT->SetAttributes(theGPT->GetPartNum());
-           else
-               printf("No partitions\n");
-            break;
-         case 'c': case 'C':
-            if (theGPT->GetPartRange(&temp1, &temp2) > 0) {
-               pn = theGPT->GetPartNum();
-               printf("Enter the partition's new unique GUID:\n");
-               theGPT->SetPartitionGUID(pn, GetGUID());
-            } else printf("No partitions\n");
-            break;
-         case 'd': case 'D':
-            printf("The number of logical sectors per physical sector is %d.\n",
-                   theGPT->GetAlignment());
-            break;
-         case 'e': case 'E':
-            printf("Relocating backup data structures to the end of the disk\n");
-            theGPT->MoveSecondHeaderToEnd();
-            break;
-         case 'g': case 'G':
-            printf("Enter the disk's unique GUID:\n");
-            theGPT->SetDiskGUID(GetGUID());
-            break;
-         case 'i': case 'I':
-            theGPT->ShowDetails();
-            break;
-         case 'l': case 'L':
-            temp1 = GetNumber(1, 128, 8, "Enter the number of logical sectors in a physical sector on the\ndisk (1-128, default = 8): ");
-            theGPT->SetAlignment(temp1);
-            break;
-         case 'm': case 'M':
-            MainMenu(filename, theGPT);
-            goOn = 0;
-            break;
-         case 'n': case 'N':
-            theGPT->MakeProtectiveMBR();
-            break;
-         case 'o': case 'O':
-            theGPT->DisplayMBRData();
-            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->ResizePartitionTable();
-            break;
-         case 'v': case 'V':
-            theGPT->Verify();
-            break;
-         case 'w': case 'W':
-            if (theGPT->SaveGPTData() == 1) {
-               goOn = 0;
-            } // if
-            break;
-         case 'z': case 'Z':
-            if (theGPT->DestroyGPT() == 1) {
-               goOn = 0;
-            }
-            break;
-         default:
-            ShowExpertCommands();
-            break;
-      } // switch
-   } while (goOn);
-} // ExpertsMenu()
+   sscanf(Info.substr(startPos, endPos - startPos + 1).c_str(), "%llu", &retval);
+/*   printf("In GetInt(), startPos = %d, endPos = %d, retval = %llu\n", startPos,
+          endPos, (unsigned long long) retval); */
+   return retval;
+} // GetInt()
 
-void ShowExpertCommands(void) {
-   printf("a\tset attributes\n");
-   printf("c\tchange partition GUID\n");
-   printf("d\tdisplay the number of logical sectors per physical sector\n");
-   printf("e\trelocate backup data structures to the end of the disk\n");
-   printf("g\tchange disk GUID\n");
-   printf("i\tshow detailed information on a partition\n");
-   printf("b\tset the number of logical sectors per physical sector\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\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\tzap (destroy) GPT data structures and exit\n");
-   printf("?\tprint this menu\n");
-} // ShowExpertCommands()
-*/
+// Extract string data from argument string, which should be colon-delimited
+string GetString(char* argument, int itemNum) {
+   int startPos = -1, endPos = -1;
+   string Info;
+
+   Info = argument;
+   while (itemNum-- > 0) {
+      startPos = endPos + 1;
+      endPos = Info.find(':', startPos);
+   }
+   if (endPos == string::npos)
+      endPos = Info.length();
+   endPos--;
+
+   return Info.substr(startPos, endPos - startPos + 1);
+} // GetString()