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/CHANGELOG b/CHANGELOG
index 0cf5148..f8accb7 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,3 +1,45 @@
+0.5.0:
+------
+
+- Added GPT-to-MBR conversion function. It's very limited, but potentially
+  useful in some cases.
+
+- Fixed bug that caused incorrect file sizes to be reported on 32-bit
+  Linux, thus causing problems when editing partition tables in disk images
+  or when loading GPT backup files.
+
+- Fixed bug that caused bogus CRC error reports when loading backup GPT
+  data.
+
+- Reorganized menus. There are now three: the main menu, the experts' menu,
+  and the recovery & transformation menu. The last of these has most of the
+  items that had been on the earlier versions' experts' menu.
+
+- Added ability to re-load the MBR and generate a fresh GPT from it. This
+  is normally identical to quitting and re-running the program, but it
+  could be handy if, say, the GPT partitions on a hybrid configuration are
+  badly messed up; this will enable using the hybridized partitions as the
+  starting point for a new GPT setup.
+
+- The program now generates CHS values for hybrid and GPT-to-MBR conversion
+  MBRs. For the moment, the assumption is the maximum number of heads and
+  sectors per track (255 and 63, respectively), although the bulk of the
+  code supports other values -- it'd just be awkward to enter the data in
+  the user interface.
+
+- Fixed minor display bug that caused number of sectors on the disk to be
+  shown as 0 on large disks when running 32-bit binaries.
+
+- Reverted 0.4.2's zap (destroy GPT) changes, since I don't want to wipe
+  out a valid MBR if the user created that MBR over an older GPT without
+  first properly wiping out the GPT, and the user now wants to wipe out
+  the GPT.
+
+- Reformatted and edited the man page. Aside from edits related to the
+  preceding program changes, I've altered the markup slightly and trimmed
+  much of the more tutorial information from the man page to better
+  conform to typical terse man page style.
+
 0.4.2:
 ------
 
diff --git a/Makefile b/Makefile
index f9467ed..7484b89 100644
--- a/Makefile
+++ b/Makefile
@@ -28,6 +28,6 @@
 depend: $(SRCS)
 	$(DEPEND) $(SRCS)
 
-$(OBJS):	
+$(OBJS):
 
 # DO NOT DELETE
diff --git a/gdisk.8 b/gdisk.8
index 143869c..d370249 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -1,259 +1,124 @@
 .\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
 .\" May be distributed under the GNU General Public License
-.TH GDISK 8 "August 2009" "0.4.2" "GPT fdisk Manual"
-.SH NAME
+.TH "GDISK" "8" "0.5.0" "Roderick W. Smith" "GPT fdisk Manual"
+.SH "NAME"
 gdisk \- GPT partition table manipulator for Linux and Unix
-.SH SYNOPSIS
+.SH "SYNOPSIS"
 .BI "gdisk "
 [ \-l ]
 .I device
 
-.SH DESCRIPTION
+.SH "DESCRIPTION"
 
-Hard disks can be divided into one or more segments, known as
-.IR partitions .
-This division is described in the
-.I "partition table"
-of the disk. Several different partition table formats exist, each with its
-advantages and disadvantages.
+GPT fdisk (aka \fBgdisk\fR) is a text\-mode menu\-driven program for
+creation and manipulation of partition tables. It 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 GPT
+partition table. When used with the \fI\-l\fR command\-line option, the
+program displays the current partition table and then exits.
 
-The original partitioning system used on PCs, now known as the
-.IR "MBR partitioning scheme",
-is subject to several limitations. These include an awkward distinction
-between
-.IR "primary",
-.IR "extended",
-and
-.IR "logical"
-partitions; no redundancy or error correction capabilities; and 32-bit data
-structures that, in conjunction with the common 512-byte sector size,
-impose a hard 2 TiB limit on the size of partitions and disks. This final
-drawback makes MBR partitions unsuitable for use on large hardware RAID
-arrays. Individual disk sizes are expected to surpass the 2 TiB limit in
-2009, so MBR will become an unsuitable partitioning system even for
-individual hard disks in the near future.
-
-The successor to MBR partitions is the
-.IR "Globally Unique Identifier (GUID) Partition Table (GPT)"
-system. GPT addresses each of the major limitations of MBR partitions, and
-includes a dummy MBR partition table with a single
-.IR "protective MBR"
-entry to keep GPT-unaware programs from modifying the disk's GPT partitions. GPT
-is a new partitioning scheme, though, and as such, older utilities and OSes
-must be replaced or modified to handle GPT. Linux's venerable
-.B "fdisk"
-program, in particular, cannot process GPT disks. (The same is true of
-related programs, such as
-.B "sfdisk"
-and
-.BR "cfdisk".)
-The alternative GNU
-Parted and related programs, however, are capable of working on both MBR
-and GPT disks.
-
-GPT is often associated with the
-.IR "Extensible Firmware Interface (EFI)",
-which is Intel's intended successor to the traditional (legacy) PC BIOS. It
-is possible to use and even boot from GPT disks on non-EFI systems,
-including those that use a legacy BIOS. Using GPT disks on such a system
-isn't a great challenge, although the OS must support GPT. Booting from a
-GPT-based disk requires that the OS support this action, and if the system
-is BIOS-based, a GPT-aware boot loader is required. Patched versions of the
-.IR "Grand Unified Bootloader (GRUB)"
-0.97, as well as GRUB2, support GPT.
-
-GPT creates five distinct data structures of three types:
-.TP
-.B "Protective MBR"
-The first sector (512 bytes) of the disk is devoted to an MBR that
-consists of a single partition spanning the entire disk (or 2 TiB for disks
-larger than this). The protective MBR may optionally include first-stage
-boot loader code.
-.TP
-.B "GPT headers"
-Two GPT headers exist, a main header and a backup header. The primary
-header resides immediately after the protective MBR, and the backup header
-is stored on the last sector of the disk. These headers contain disk
-metadata, such as the location of the partition table, the size of the
-partition table, and a "serial number" (GUID) that should be unique for
-each disk. Each GPT header also stores two CRC checksums, one for the
-partition table and one for the GPT header itself.
-.TP
-.B "Partition tables"
-Each GPT header points to one partition table. The main partition table
-appears immediately after the main GPT header, and the backup partition
-table comes immediately before its GPT header. Typically, the partition
-tables may hold data on up to 128 partitions, although
-.B gdisk
-enables you to change this value. Each entry contains 64-bit start and stop
-sector numbers, a name, a partition GUID type code, a unique partition GUID
-identifier, and additional data.
-.PP
-
-The GPT fdisk (aka
-.BR "gdisk")
-program operates mainly on the GPT headers and partition tables; however,
+GPT fdisk operates mainly on the GPT headers and partition tables; however,
 it can and will generate a fresh protective MBR, when required. (Any boot
 loader code in the protective MBR will not be disturbed.) If you've created
 an unusual protective MBR, such as a hybrid MBR created by
-.IR "gptsync"
-or
-.BR "gdisk"'s
-own hybrid MBR creation feature,
+\fBgptsync\fR or \fBgdisk\fR's own hybrid MBR creation feature,
 this should not be disturbed by most ordinary actions. Some advanced data
 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.
+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
-.B "gdisk"
-program employs a user interface similar to that of Linux's
-.BR "fdisk",
-but
-.B "gdisk"
+The \fBgdisk\fR program employs a user interface similar to that of Linux's
+\fBfdisk\fR, but \fBgdisk\fR
 modifies GPT partitions. It also has the capability of transforming MBR
 partitions or BSD disklabels into GPT partitions. Like the original
-.B fdisk
-program,
-.B gdisk
+\fBfdisk\fR program, \fBgdisk\fR
 does not modify disk structures until you explicitly write them to disk, so
 if you make a mistake, you can exit from the program with the 'q' option to
 save your partitions.
 
-.B gdisk
-is a text-mode menu-driven program for creation and manipulation of
-partition tables. It will automatically convert an MBR partition table or
-BSD disklabel stored without an MBR carrier partition to GPT format, or
-will load a GPT partition table. When used with the
-.IR "\-l"
-command-line option, the program displays the current partition table and
-then exits.
-
-Linux hard disk device filenames take the form
-.IR "/dev/sdx"
-or
-.IR "/dev/hdx",
-where
-.IR "x"
-is a letter from
-.IR "a"
-onward. The
-.IR "hdx"
-devices originally referred to IDE (aka PATA) drives, whereas
-.IR "sdx"
-devices originally referred to SCSI drives. These distinctions are now
-blurring. Modern SATA drives and USB flash drives usually acquire
-.IR "sdx"
-names, and the same can even be true of PATA drives, depending on kernel
-driver options. For instance,
-.IR "/dev/hda"
-refers to the first PATA drive, whereas
-.IR "/dev/sdb"
-is the second SCSI, SATA, USB, or other SCSI-equivalent drive. To use
-.BR "gdisk",
-you must pass a device filename to the program on the command line.
-
-The
-.I partition
-is a
-device name followed by a partition number.  For example,
-.B /dev/hda1
-is the first partition on the first PATA hard disk.
-.B gdisk
-creates partitions, but you don't pass partition numbers or partition
-device filenames to the program. Linux generates numbers for GPT partitions
-based on the partition's position in the partition table.
+Ordinarily, \fBgdisk\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; \fBgdisk\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
+and limiting. GPT drops CHS addressing and uses 64\-bit LBA mode
 exclusively. Thus, GPT data structures, and therefore
-.BR "gdisk",
-do not need to deal with CHS geometries and all the problems they create.
-Users of
-.BR "fdisk"
-will note that
-.B "gdisk"
+\fBgdisk\fR, do not need to deal with CHS geometries and all the problems
+they create. Users of \fBfdisk\fR will note that \fBgdisk\fR
 lacks the options and limitations associated with CHS geometries.
 
-For best results, you should always use an OS-specific partition table
-program.  For example, you should make Mac OS X partitions with the Mac OS
-X Disk Utility
-program and Linux partitions with the Linux
-.B "gdisk"
-or GNU Parted program.
+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 or GNU Parted program.
 
-Upon start,
-.B gdisk
-attempts to identify the partition type in use on the specified disk. If it
-finds valid GPT data,
-.B gdisk
-will use it. If
-.B gdisk
+Upon start, \fBgdisk\fR attempts to identify the partition type in use
+on the disk. If it finds valid GPT data, \fBgdisk\fR
+will use it. If \fBgdisk\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. Upon exiting with the 'w' option,
-.B gdisk
-will then replace the MBR or disklabel with a GPT.
-.IR "This action is potentially dangerous!"
+Apple Partition Map (APM) disks, which are used on 680x0\- and PowerPC\-based
+Macintoshes. Upon exiting with the 'w' option, \fBgdisk\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. If you
-mistakenly launch
-.B gdisk
-on an MBR disk, you can safely exit the program
-without making any changes by using the 'q' option.
+particularly likely if you're multi\-booting with any GPT\-unaware OS. If you
+mistakenly launch \fBgdisk\fR on an MBR disk, you can safely exit
+the program without making any changes by using the 'q' option.
 
-The MBR-to-GPT conversion will leave at least one gap in the partition
+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 's' option, if you like.
-(Doing this may require you to update your
-.IR "/etc/fstab"
-file.)
+(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
+.TP 
 .B *
-For data (non-boot) disks, and for boot disks used on BIOS-based computers
+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
+.TP 
 .B *
-Boot disks for EFI-based systems require an "EFI System
-Partition" (
-.B "gdisk"
-internal code 0xEF00) formatted as FAT-32. The recommended size of this
-partition is 100 MiB. Boot-related files are stored here. (Note that GNU
-Parted identifies such partitions as having the "boot flag" set.)
+Boot disks for EFI\-based systems require an \fIEFI System
+Partition\fR (\fBgdisk\fR internal code 0xEF00) formatted as FAT\-32.
+The recommended size of this partition 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
+.TP 
 .B *
-Some boot loaders for BIOS-based systems make use of a "BIOS Boot
-Partition" (
-.B "gdisk"
+Some boot loaders for BIOS\-based systems make use of a \fIBIOS Boot
+Partition\fR (\fBgdisk\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 (a few tens of kilobytes), but you should
 consult your boot loader documentation for details.
 
-.TP
+.TP 
 .B *
-If Windows is to boot from a GPT disk, a partition of type "Microsoft
-Reserved" (
-.B "gdisk"
+If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft
+Reserved\fR (\fBgdisk\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
+.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
@@ -262,187 +127,165 @@
 positioning option (specifying the starting sector as '+128M', for
 instance) to simplify creating such gaps.
 
-.SH OPTIONS
-.TP
+.SH "OPTIONS"
+.TP 
 .B \-l
 List the partition tables for the specified devices and then exit.
-.PP
+.PP 
 
-Most interactions with
-.B gdisk
-occur with its interactive text-mode menus. The main menu provides the
-following options:
+Most interactions with \fBgdisk\fR
+occur with its interactive text\-mode menus. Three menus exist: the main
+menu, the recovery & transformation menu, and the experts' menu. The main
+menu provides the functions that are most likely to be useful for typical partitioning tasks, such as creating and deleting partitions, changing partition type codes, and so on. Specific functions are:
 
-.TP
+.TP 
 .B b
-Convert 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.
-.B gdisk
-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 GPT fdisk being unable to convert a BSD disklabel are high
-compared to the likelihood of problems with an MBR conversion.
+Save partition data to a backup file. You can back up your 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
+restore option is on the recovery & transformation menu; the backup
+option is on the main menu to encourage its use.
 
-.TP
+
+.TP 
 .B c
-Change the GPT name of a partition. This name is encoded as a UTF-16
-string, but
-.B gdisk
+Change the GPT name of a partition. This name is encoded as a UTF\-16
+string, but \fBgdisk\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.
 
-.TP
+.TP 
 .B d
 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,
-.B gdisk
-deletes it, as well, and expands any adjacent 0xEE (EFI GPT) MBR protective
-partition to fill the new free space.
+\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT)
+MBR protective partition to fill the new free space.
 
-.TP
+.TP 
 .B i
 Show detailed partition information. The summary information produced by
 the 'p' command necessarily omits many details, such as the partition's
-unique GUID and the translation of
-.BR "gdisk"'s
+unique GUID and the translation of \fBgdisk\fR's
 internal partition type code to a plain type name. The 'i' option
 displays this information for a single partition.
 
-.TP
+.TP 
 .B l
 Display a summary of partition types. GPT uses a GUID to identify
 partition types for particular OSes and purposes. For ease of data entry,
-.B gdisk
-compresses these into two-byte (four-digit hexadecimal) values that are
-related to their 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
-.BR "gdisk".
-A one-to-one correspondence is impossible, though. Most notably, many DOS,
+\fBgdisk\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
-.BR "gdisk" ).
-Some OSes use a single MBR code but employ many more codes in GPT. For
-these,
-.B gdisk
+(entered as 0x0700 in \fBgdisk\fR). Some OSes use a single MBR code but
+employ many more codes in GPT. For these, \fBgdisk\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
-.BR "gdisk".
+these two\-byte codes are unique to \fBgdisk\fR.
 
-.TP
-.B m
-Print the menu. Type this command (or any other unrecognized command) to
-see a summary of available options.
-
-.TP
+.TP 
 .B n
 Create a new partition. This command is modelled after the equivalent
-.B fdisk
-option, although some differences exist. 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,
-.BI 40M
+\fBfdisk\fR option, although some differences exist. 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 range by preceding
-the number by a '+' or '-' symbol, as in
-.BI +2G
-to specify a point 2GiB after the first available sector, or
-.BI -200M
+the number by a '+' or '\-' symbol, as in \fI\fB+2G\fR\fR
+to specify a point 2GiB after the first available 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 last available
 block for the end sector.
 
-.TP
+.TP 
 .B o
 Clear out all partition data. This includes GPT header data,
 all partition definitions, and the protective MBR.
 
-.TP
+.TP 
 .B p
 Display basic partition summary data. This includes partition
 numbers, starting and ending sector numbers, partition sizes,
-.BR "gdisk"'s
-partition types codes, and partition names. For additional information,
-use the 'i' command.
+\fBgdisk\fR's partition types codes, and partition names. For
+additional information, use the 'i' command.
 
-.TP
+.TP 
 .B q
-Quit from the program
-.IR "without saving data".
+Quit from the program \fIwithout saving your changes\fR.
 Use this option if you just wanted to view information or if you make a
 mistake and want to back out of all your changes.
 
-.TP
+.TP 
+.B r
+Enter the recovery & transformation menu. This menu includes emergency
+recovery options (to fix damaged GPT data structures) and options to
+transform to or from other partitioning systems, including creating
+hybrid MBRs.
+
+.TP 
 .B s
 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, such as GNU Parted, will sort
+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
-.IR "/etc/fstab"
-if you use this option.
+\fI/etc/fstab\fR if you use this option.
 
-.TP
+.TP 
 .B t
 Change a single partition's type code. You enter the type code using a
-two-byte hexadecimal number, as described earlier. You may also enter a
-GUID directly, if you have one and
-.B gdisk
-doesn't know it.
+two\-byte hexadecimal number, as described earlier. You may also enter a
+GUID directly, if you have one and \fBgdisk\fR doesn't know it.
 
-.TP
+.TP 
 .B v
 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 these problems, though; for that, you must use
-options on the experts' menu. If no problems are found, this command
-displays a summary of unallocated disk space.
+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
+.TP 
 .B w
 Write data. Use this command to save your changes.
 
-.TP
+.TP 
 .B x
 Enter the experts' menu. Using this option provides access to features you
 can use to get into even more trouble than the main menu allows.
-.PP
+.PP 
 
-A few options on the experts' menu duplicate functionality on the main
-menu, for the sake of convenience; however, for the most part the experts'
-menu provides unusually dangerous or obscure options. These are:
+.TP 
+.B ?
+Print the menu. Type this command (or any other unrecognized command) to
+see a summary of available options.
 
-.TP
-.B a
-Set attributes. GPT provides a 64-bit attributes field that can be used to
-set partition features.
-.B gdisk
-supports four attributes:
-.IR "system partition",
-.IR "read-only",
-.IR "hidden",
-and
-.IR "do not automount".
-You can set other attributes, but their numbers aren't translated into
-anything useful. In practice, most OSes seem to ignore these attributes.
-.TP
+.PP 
+The second \fBgdisk\fR menu is the recovery & transformation menu, which
+provides access to data recovery options and features related to the
+transformation of partitions between partitioning schemes (converting
+BSD disklabels into GPT partitions or creating hybrid MBRs, for instance).
+A few options on this menu duplicate functionality on the main
+menu, for the sake of convenience. The options on this menu are:
+
+.TP 
 .B b
-Rebuild main GPT header from backup. You can use the backup GPT header to
+Rebuild GPT header from backup. You can use the backup GPT header to
 rebuild the main GPT header with this option. It's likely to be useful if
 your main GPT header was damaged or destroyed (say, by sloppy use of
-.IR "dd").
-.TP
+\fBdd\fR).
+
+.TP 
 .B c
-Load backup partition table. Ordinarily,
-.B gdisk
+Load backup partition table. Ordinarily, \fBgdisk\fR
 uses only the main partition table (although the backup's integrity is
 checked when you launch the program). If the main partition table has been
 damaged, you can use this option to load the backup from disk and use it
@@ -450,85 +293,174 @@
 partition entries if you've just converted an MBR disk to GPT format, since
 there will be no backup partition table on disk.
 
-.TP
+.TP 
 .B d
 Use main GPT header and rebuild the backup. This option is likely to be
 useful if the backup GPT header has been damaged or destroyed.
-.TP
 
+.TP 
 .B e
 Load main partition table. This option reloads the main partition table
 from disk. It's only likely to be useful if you've tried to use the backup
 partition table (via 'c') but it's in worse shape then the main partition
 table.
-.TP
 
+.TP 
 .B f
-Change partition GUID. You can enter a custom unique GUID for a partition
-using this option. (Note this refers to the GUID that uniquely identifies a
-partition, not to its type code.) Ordinarily,
-.B gdisk
-assigns this number randomly; however, you might want to adjust the number
-manually if you've wound up with the same GUID on two partitions.
-.TP
+Load MBR and build fresh GPT from it. Use this option if your GPT is corrupt
+or conflicts with the MBR and you want to use the MBR as the basis for a new
+set of GPT partitions.
+
+.TP 
 .B g
-Change disk GUID. Each disk has a unique GUID code, which
-.B gdisk
-assigns randomly upon creation of the GPT data structures. You can generate
-a fresh random GUID or enter one manually with this option.
+Convert GPT into MBR and exit. This option converts up to four GPT partitions
+into MBR form, destroys the GPT data structures, saves the new MBR, and exits.
+Use this option if you've tried GPT and find that MBR works better for you.
+Note that this function generates up to four \fIprimary\fR MBR partitions;
+it cannot generate logical partitions, and so it cannot transform more than
+four partitions. If four or fewer partitions exist, and if they can be represented
+in the 32\-bit MBR LBA scheme, this function converts
+them all. If more than four partitions exist, you'll be asked to select which
+ones to convert. See also the 'h' option.
 
-.TP
+.TP 
 .B h
-Create a hybrid MBR. This is an ugly workaround that enables GPT-unaware
-OSes, or that that can't boot from a GPT disk, to access up to three of
+Create a hybrid MBR. This is an ugly workaround that enables GPT\-unaware
+OSes, or those that can't boot from a GPT disk, to access up to three of
 the partitions on the disk by creating MBR entries for them. Note that
-these hybrid MBR entries are not updated when you make subsequent changes
-to the GPT entries, so you must re-run this option whenever you make
-changes that would affect the hybridized partitions.
+these hybrid MBR entries can easily go out of sync with the GPT entries,
+particularly when hybrid\-unaware GPT utilities are used to edit the disk.
+Thus, you may need to recreate the hybrid MBR if you use such tools.
 
-.TP
+.TP 
 .B i
 Show detailed partition information. This option is identical to the 'i'
 option on the main menu.
-.TP
-.B k
-Save partition data to a backup file. You can back up your 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.
-.TP
+
+.TP 
 .B l
-Load partition data from a backup file. This option is the reverse of the 'k'
-option. Note that restoring partition data from anything but the
-original disk is not recommended.
-.TP
+Load partition data from a backup file. This option is the reverse of the 'b'
+option on the main menu. Note that restoring partition data from anything
+but the original disk is not recommended.
+
+.TP 
 .B m
-Print the menu. This option (or any unrecognized entry) displays a summary
-of the menu options.
-.TP
-.B n
-Create a new protective MBR. Use this option if the current protective MBR
-is damaged in a way that
-.B gdisk
-doesn't automatically detect and correct.
-.TP
+Return to the main menu. This option enables you to enter main\-menu commands.
+
+.TP 
 .B o
 Print protective MBR data. You can see a summary of the protective MBR's
 partitions with this option. This may enable you to spot glaring problems
 or help identify the partitions in a hybrid MBR.
-.TP
+
+.TP 
 .B p
 Print the partition table. This option is identical to the 'p' option in
 the main menu.
-.TP
+
+.TP 
 .B q
 Quit without saving changes. This option is identical to the 'q' option in
 the main menu.
-.TP
-.B r
-Return to the main menu. You can go back to the main menu with this option.
 
-.TP
+.TP 
+.B t
+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. \fBgdisk\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 \fBgdisk\fR being unable to convert a BSD disklabel is
+high compared to the likelihood of problems with an MBR conversion.
+
+.TP 
+.B v
+Verify disk. This option is identical to the 'v' option in the main menu.
+
+.TP 
+.B w
+Write table to disk and exit. This option is identical to the 'w' option in
+the main menu.
+
+.TP 
+.B x
+Enter the experts' menu. This option is identical to the 'x' option in the
+main menu.
+
+.TP 
+.B ?
+Print the menu. This option (or any unrecognized entry) displays a summary
+of the menu options.
+
+.PP 
+The third \fBgdisk\fR menu is the experts' menu. This menu provides advanced
+options that aren't closely related to recovery or transformation between
+partitioning systems. Its options are:
+
+.TP 
+.B a
+Set attributes. GPT provides a 64\-bit attributes field that can be used to
+set features for each partition. \fBgdisk\fR supports four attributes:
+\fIsystem partition\fR, \fIread\-only\fR, \fIhidden\fR, and
+\fIdo not automount\fR. You can set other attributes, but their numbers
+aren't translated into anything useful. In practice, most OSes seem to
+ignore these attributes.
+
+.TP 
+.B c
+Change partition GUID. You can enter a custom unique GUID for a partition
+using this option. (Note this refers to the GUID that uniquely identifies a
+partition, not to its type code, which you can change with the 't' main\-menu
+option.) Ordinarily, \fBgdisk\fR assigns this number randomly; however,
+you might want to adjust the number manually if you've wound up with the
+same GUID on two partitions because of buggy GUID assignments (hopefully
+not in \fBgdisk\fR) or sheer incredible coincidence.
+
+.TP 
+.B g
+Change disk GUID. Each disk has a unique GUID code, which \fBgdisk\fR
+assigns randomly upon creation of the GPT data structures. You can generate
+a fresh random GUID or enter one manually with this option.
+
+.TP 
+.B i
+Show detailed partition information. This option is identical to the 'i'
+option on the main menu.
+
+.TP 
+.B m
+Return to the main menu. This option enables you to enter main\-menu commands.
+
+.TP 
+.B n
+Create a new protective MBR. Use this option if the current protective MBR
+is damaged in a way that \fBgdisk\fR doesn't automatically detect and
+correct, or if you want to convert a hybrid MBR into a "pure" GPT with a
+conventional protective MBR.
+
+.TP 
+.B o
+Print protective MBR data. You can see a summary of the protective MBR's
+partitions with this option. This may enable you to spot glaring problems
+or help identify the partitions in a hybrid MBR.
+
+.TP 
+.B p
+Print the partition table. This option is identical to the 'p' option in
+the main menu.
+
+.TP 
+.B q
+Quit without saving changes. This option is identical to the 'q' option in
+the main menu.
+
+.TP 
+.B r
+Enter the recovery & transformations menu. This option is identical to
+the 'r' option on the main menu.
+
+.TP 
 .B s
 Resize partition table. The default partition table size is 128 entries.
 Officially, sizes of less than 16KB (128 entries, given the normal entry
@@ -537,128 +469,122 @@
 sizes also work fine. OSes may impose their own limits on the number of
 partitions, though.
 
-.TP
+.TP 
 .B v
 Verify disk. This option is identical to the 'v' option in the main menu.
-.TP
-.B w
-Write table to disk and exit. This option is identical to the 'w' option in
-the main menu.
 
-.TP
+.TP 
 .B z
-Destroy the GPT data structures and exit. Use this option if you want to
-repartition a GPT disk using
-.B "fdisk"
-or some other GPT-unaware program.
+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.
+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.
 
-.PP
+.TP 
+.B ?
+Print the menu. This option (or any unrecognized entry) displays a summary
+of the menu options.
 
+.PP 
 In many cases, you can press the Enter key to select a default option when
-entering data. When only one option is possible,
-.B gdisk
+entering data. When only one option is possible, \fBgdisk\fR
 usually bypasses the prompt entirely.
 
-.SH BUGS
-As of September 2009 (version 0.4.2),
-.B gdisk
+.SH "BUGS"
+As of September 2009 (version 0.5.0), \fBgdisk\fR
 should be considered beta software. Known bugs and limitations include:
 
-.TP
+.TP 
 .B *
-The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Both
-64-bit (x86-64) and 32-bit (x86) versions for Linux have been tested, the
-former more thoroughly than the latter. The Mac OS X support was added with
-version 0.3.1 and has not been as thoroughly tested. FreeBSD support was
-added with version 0.4.0 and has not been very thoroughly tested.
+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. The Mac OS X
+support was added with version 0.3.1 and has not been as thoroughly tested.
+FreeBSD support was added with version 0.4.0 and has seen even less
+testing.
 
-.TP
+.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
-.B "gpt"
-,
-.B "fdisk"
-, and
-.B "dd".
+\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.)
 
-
-.TP
+.TP 
 .B *
 The fields used to display the start and end sector numbers for partitions
 in the 'p' command 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
+.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' menu option's display; however, they should be
+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
+.TP 
 .B *
-The program can load only up to 124 logical partitions when converting from
-MBR format. This limit can be raised by changing the #define NUM_LOGICALS
-line in the
-.IR "mbr.cc"
-source code file and recompiling; however, such a change will require using
-a larger-than-normal GPT partition table. (The limit of 124 logical
-partitions was chosen because that number plus the four primary partitions
-equals the 128 partitions supported by the most common GPT partition table
-size.)
+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 GPT partition table. (The limit
+of 128 partitions was chosen because that number equals the 128 partitions
+supported by the most common GPT partition table size.)
 
-.TP
+.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.
+partition using GNU Parted or a similar tool prior to conversion with
+\fBgdisk\fR.
 
-.TP
+.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
+.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
+.TP 
 .B *
 Because of the highly variable nature of BSD disklabel structures,
-conversions from this form may be unreliable -- partitions may be dropped,
+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
+.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
+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 experts'
-menu) or abandoning GPT in favor of MBR may be your only options in this
-case.
+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
+.PP 
 
-The support for big-endian CPUs (PowerPC, for example) is new, as of version
+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
-
+.SH "AUTHORS"
 Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
 
 Contributors:
@@ -668,18 +594,18 @@
 * David Hubbard (david.c.hubbard@gmail.com)
 
 .SH "SEE ALSO"
-.BR cfdisk (8),
-.BR fdisk (8),
-.BR mkfs (8),
-.BR parted (8),
-.BR sfdisk (8)
+\fBcfdisk (8)\fR,
+\fBfdisk (8)\fR,
+\fBmkfs (8)\fR,
+\fBparted (8)\fR,
+\fBsfdisk (8)\fR
 
-.IR "http://en.wikipedia.org/wiki/GUID_Partition_Table"
+\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
 
-.IR "http://developer.apple.com/technotes/tn2006/tn2166.html"
+\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR
 
-.IR "http://www.rodsbooks.com/gdisk/"
+\fIhttp://www.rodsbooks.com/gdisk/\fR
 
-.SH AVAILABILITY
-The gdisk command is part of the GPT fdisk package and is available from
-Rod Smith.
+.SH "AVAILABILITY"
+The \fBgdisk\fR command is part of the \fIGPT fdisk\fR package and is
+available from Rod Smith.
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()
diff --git a/gpt.cc b/gpt.cc
index 3b7f9d9..5885486 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -112,7 +112,24 @@
             "table when you save your partitions.\n");
    } // if
 
-   // Now check that critical main and backup GPT entries match
+   // Now check that the main and backup headers both point to themselves....
+   if (mainHeader.currentLBA != 1) {
+      problems++;
+      printf("\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
+             "is being automatically corrected, but it may be a symptom of more serious\n"
+             "problems. Think carefully before saving changes with 'w' or using this disk.\n");
+      mainHeader.currentLBA = 1;
+   } // if
+   if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
+      problems++;
+      printf("\nProblem: The secondary header's self-pointer doesn't point to itself. This\n"
+             "problem is being automatically corrected, but it may be a symptom of more\n"
+             "serious problems. Think carefully before saving changes with 'w' or using this\n"
+             "disk.\n");
+      secondHeader.currentLBA = diskSize - UINT64_C(1);
+   } // if
+
+   // Now check that critical main and backup GPT entries match each other
    if (mainHeader.currentLBA != secondHeader.backupLBA) {
       problems++;
       printf("\nProblem: main GPT header's current LBA pointer (%llu) doesn't\n"
@@ -295,34 +312,38 @@
 // Note: Must be called BEFORE byte-order reversal on big-endian
 // systems!
 int GPTData::CheckHeaderCRC(struct GPTHeader* header) {
-   uint32_t oldCRC, newCRC;
+   uint32_t oldCRC, newCRC, hSize;
 
    // Back up old header CRC and then blank it, since it must be 0 for
    // computation to be valid
    oldCRC = header->headerCRC;
-   if (IsLittleEndian() == 0)
-      ReverseBytes(&oldCRC, 4);
    header->headerCRC = UINT32_C(0);
+   hSize = header->headerSize;
+
+   // If big-endian system, reverse byte order
+   if (IsLittleEndian() == 0) {
+      ReverseBytes(&oldCRC, 4);
+   } // if
 
    // Initialize CRC functions...
    chksum_crc32gentab();
 
    // Compute CRC, restore original, and return result of comparison
    newCRC = chksum_crc32((unsigned char*) header, HEADER_SIZE);
-   mainHeader.headerCRC = oldCRC;
+   header->headerCRC = oldCRC;
    return (oldCRC == newCRC);
 } // GPTData::CheckHeaderCRC()
 
 // Recompute all the CRCs. Must be called before saving (but after reversing
 // byte order on big-endian systems) if any changes have been made.
 void GPTData::RecomputeCRCs(void) {
-   uint32_t crc;
-   uint32_t trueNumParts;
+   uint32_t crc, hSize, trueNumParts;
    int littleEndian = 1;
 
    // Initialize CRC functions...
    chksum_crc32gentab();
 
+   hSize = mainHeader.headerSize;
    littleEndian = IsLittleEndian();
 
    // Compute CRC of partition tables & store in main and secondary headers
@@ -342,11 +363,11 @@
    secondHeader.headerCRC = 0;
 
    // Compute & store CRCs of main & secondary headers...
-   crc = chksum_crc32((unsigned char*) &mainHeader, HEADER_SIZE);
+   crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
    if (littleEndian == 0)
       ReverseBytes(&crc, 4);
    mainHeader.headerCRC = crc;
-   crc = chksum_crc32((unsigned char*) &secondHeader, HEADER_SIZE);
+   crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
    if (littleEndian == 0)
       ReverseBytes(&crc, 4);
    secondHeader.headerCRC = crc;
@@ -359,7 +380,7 @@
 
    mainHeader.signature = GPT_SIGNATURE;
    mainHeader.revision = secondHeader.revision;
-   mainHeader.headerSize = HEADER_SIZE;
+   mainHeader.headerSize = secondHeader.headerSize;
    mainHeader.headerCRC = UINT32_C(0);
    mainHeader.reserved = secondHeader.reserved;
    mainHeader.currentLBA = secondHeader.backupLBA;
@@ -382,7 +403,7 @@
 
    secondHeader.signature = GPT_SIGNATURE;
    secondHeader.revision = mainHeader.revision;
-   secondHeader.headerSize = HEADER_SIZE;
+   secondHeader.headerSize = mainHeader.headerSize;
    secondHeader.headerCRC = UINT32_C(0);
    secondHeader.reserved = mainHeader.reserved;
    secondHeader.currentLBA = mainHeader.backupLBA;
@@ -486,12 +507,6 @@
       printf("It will be destroyed if you continue!\n");
       printf("*******************************************************************\n\n\a");
    } // if
-/*   if (bsdFound) {
-   printf("\n*************************************************************************\n");
-   printf("This disk appears to contain a BSD disklabel! It will be destroyed if you\n"
-   "continue!\n");
-   printf("*************************************************************************\n\n\a");
-} // if */
 } // GPTData::PartitionScan()
 
 // Read GPT data from a disk.
@@ -703,7 +718,7 @@
 // Writes GPT (and protective MBR) to disk. Returns 1 on successful
 // write, 0 if there was a problem.
 int GPTData::SaveGPTData(void) {
-   int allOK = 1, i;
+   int allOK = 1;
    char answer, line[256];
    int fd;
    uint64_t secondTable;
@@ -856,6 +871,12 @@
          ReverseHeaderBytes(&secondHeader);
       } // if
 
+      // Recomputing the CRCs is likely to alter them, which could be bad
+      // if the intent is to save a potentially bad GPT for later analysis;
+      // but if we don't do this, we get bogus errors when we load the
+      // backup. I'm favoring misses over false alarms....
+      RecomputeCRCs();
+
       // Now write the protective MBR...
       protectiveMBR.WriteMBRData(fd);
 
@@ -1021,8 +1042,8 @@
    uint64_t temp, totalFree;
 
    BytesToSI(diskSize * blockSize, sizeInSI);
-   printf("Disk %s: %lu sectors, %s\n", device,
-          (unsigned long) diskSize, sizeInSI);
+   printf("Disk %s: %llu sectors, %s\n", device,
+          (unsigned long long) diskSize, sizeInSI);
    printf("Disk identifier (GUID): %s\n", GUIDToStr(mainHeader.diskGUID, tempStr));
    printf("Partition table holds up to %lu entries\n", (unsigned long) mainHeader.numParts);
    printf("First usable sector is %lu, last usable sector is %lu\n",
@@ -1033,7 +1054,7 @@
           BytesToSI(totalFree * (uint64_t) blockSize, sizeInSI));
    printf("\nNumber  Start (sector)    End (sector)  Size       Code  Name\n");
    for (i = 0; i < mainHeader.numParts; i++) {
-      partitions[i].ShowSummary(i, blockSize, sizeInSI);
+      partitions[i].ShowSummary(i, blockSize);
    } // for
 } // GPTData::DisplayGPTData()
 
@@ -1212,20 +1233,24 @@
 
 // This function destroys the on-disk GPT structures. Returns 1 if the
 // user confirms destruction, 0 if the user aborts.
-int GPTData::DestroyGPT(void) {
+// If prompt == 0, don't ask user about proceeding and do NOT wipe out
+// MBR. (Set prompt == 0 when doing a GPT-to-MBR conversion.)
+int GPTData::DestroyGPT(int prompt) {
    int fd, i;
-   char blankSector[512], goOn;
+   char blankSector[512], goOn = 'Y', blank = 'N';
 
    for (i = 0; i < 512; i++) {
       blankSector[i] = '\0';
    } // for
 
-   if ((apmFound) || (bsdFound)) {
+   if (((apmFound) || (bsdFound)) && prompt) {
       printf("WARNING: APM or BSD disklabel structures detected! This operation could\n"
              "damage any APM or BSD partitions on this disk!\n");
    } // if APM or BSD
-   printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
-   goOn = GetYN();
+   if (prompt) {
+      printf("\a\aAbout to wipe out GPT on %s. Proceed? ", device);
+      goOn = GetYN();
+   } // if
    if (goOn == 'Y') {
       fd = open(device, O_WRONLY);
 #ifdef __APPLE__
@@ -1245,18 +1270,21 @@
             write(fd, blankSector, 512);
          lseek64(fd, secondHeader.currentLBA * 512, SEEK_SET); // seek to GPT header
          write(fd, blankSector, 512); // blank it out
-         printf("Blank out MBR? ");
-         if (GetYN() == 'Y') {
+         if (prompt) {
+            printf("Blank out MBR? ");
+            blank = GetYN();
+         }// if
+         // Note on below: Touch the MBR only if the user wants it completely
+         // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
+         // the MBR, but this could wipe out a valid MBR that the program
+         // had subsequently discarded (say, if it conflicted with older GPT
+         // structures).
+         if (blank == 'Y') {
             lseek64(fd, 0, SEEK_SET);
             write(fd, blankSector, 512); // blank it out
-         } else { // write current protective MBR, in case it's hybrid....
-            // find and delete 0xEE partitions in MBR
-            for (i = 0; i < 4; i++) {
-               if (protectiveMBR.GetType(i) == (uint8_t) 0xEE) {
-                  protectiveMBR.DeletePartition(i);
-               } // if
-            } // for
-            protectiveMBR.WriteMBRData(fd);
+         } else {
+            printf("MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
+                   "with fdisk or another tool.\n");
          } // if/else
          DiskSync(fd);
          close(fd);
@@ -1374,8 +1402,8 @@
    ClearGPTData();
 
    // Convert the smaller of the # of GPT or MBR partitions
-   if (mainHeader.numParts > (NUM_LOGICALS + 4))
-      numToConvert = NUM_LOGICALS + 4;
+   if (mainHeader.numParts > (MAX_MBR_PARTS))
+      numToConvert = MAX_MBR_PARTS;
    else
       numToConvert = mainHeader.numParts;
 
@@ -1467,15 +1495,102 @@
    return numDone;
 } // GPTData::XFormDisklabel(BSDData* disklabel)
 
+// Add one GPT partition to MBR. Used by XFormToMBR() and MakeHybrid()
+// functions. Returns 1 if operation was successful.
+int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
+   int allOK = 1, typeCode, bootable;
+   uint64_t length;
+   char line[255];
+
+   if ((mbrPart < 0) || (mbrPart > 3)) {
+      printf("MBR partition %d is out of range; omitting it.\n", mbrPart + 1);
+      allOK = 0;
+   } // if
+   if (gptPart >= mainHeader.numParts) {
+      printf("GPT partition %d is out of range; omitting it.\n", gptPart + 1);
+      allOK = 0;
+   } // if
+   if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
+      printf("GPT partition %d is undefined; omitting it.\n", gptPart + 1);
+      allOK = 0;
+   } // if
+   if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
+       (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
+      if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
+         printf("Caution: Partition end point past 32-bit pointer boundary;"
+                " some OSes may\nreact strangely.\n");
+      } // if partition ends past 32-bit (usually 2TiB) boundary
+      do {
+         printf("Enter an MBR hex code (default %02X): ",
+                  typeHelper.GUIDToID(partitions[gptPart].GetType()) / 256);
+         fgets(line, 255, stdin);
+         sscanf(line, "%x", &typeCode);
+         if (line[0] == '\n')
+            typeCode = partitions[gptPart].GetHexType() / 256;
+      } while ((typeCode <= 0) || (typeCode > 255));
+      printf("Set the bootable flag? ");
+      bootable = (GetYN() == 'Y');
+      length = partitions[gptPart].GetLengthLBA();
+      protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
+                             (uint32_t) length, typeCode, bootable);
+   } else { // partition out of range
+      printf("Partition %d begins beyond the 32-bit pointer limit of MBR "
+             "partitions, or is\n too big; omitting it.\n", gptPart + 1);
+      allOK = 0;
+   } // if/else
+   return allOK;
+} // GPTData::OnePartToMBR()
+
+// Convert the GPT to MBR form. This function is necessarily limited; it
+// handles at most four partitions and creates layouts that ignore CHS
+// geometries. Returns the number of converted partitions; if this value
+// is over 0, the calling function should call DestroyGPT() to destroy
+// the GPT data, and then exit.
+int GPTData::XFormToMBR(void) {
+   char line[255];
+   int i, j, numParts, numConverted = 0;
+   uint32_t partNums[4];
+
+   // Get the numbers of up to four partitions to add to the
+   // hybrid MBR....
+   numParts = CountParts();
+   printf("Counted %d partitions.\n", numParts);
+
+   // Prepare the MBR for conversion (empty it of existing partitions).
+   protectiveMBR.EmptyMBR(0);
+   protectiveMBR.SetDiskSize(diskSize);
+
+   if (numParts > 4) { // Over four partitions; engage in triage
+      printf("Type from one to four GPT partition numbers, separated by spaces, to be\n"
+            "used in the MBR, in sequence: ");
+      fgets(line, 255, stdin);
+      numParts = sscanf(line, "%d %d %d %d", &partNums[0], &partNums[1],
+                        &partNums[2], &partNums[3]);
+   } else { // Four or fewer partitions; convert them all
+      i = j = 0;
+      while ((j < numParts) && (i < mainHeader.numParts)) {
+         if (partitions[i].GetFirstLBA() > 0) { // if GPT part. is defined
+            partNums[j++] = ++i; // flag it for conversion
+         } else i++;
+      } // while
+   } // if/else
+
+   for (i = 0; i < numParts; i++) {
+      j = partNums[i] - 1;
+      printf("\nCreating entry for partition #%d\n", j + 1);
+      numConverted += OnePartToMBR(j, i);
+   } // for
+   return numConverted;
+} // GPTData::XFormToMBR()
+
 // Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
 // OSes that don't understand GPT.
 void GPTData::MakeHybrid(void) {
    uint32_t partNums[3];
    char line[255];
-   int numParts, i, j, typeCode, bootable, mbrNum;
-   uint64_t length;
+   int numParts, numConverted = 0, i, j, typeCode, mbrNum;
    char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
-   char eeFirst; // Whether EFI GPT (0xEE) partition comes first in table
+   char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
 
    printf("\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
          "to use one, just hit the Enter key at the below prompt and your MBR\n"
@@ -1500,38 +1615,14 @@
    for (i = 0; i < numParts; i++) {
       j = partNums[i] - 1;
       printf("\nCreating entry for partition #%d\n", j + 1);
-      if ((j >= 0) && (j < mainHeader.numParts)) {
-         if ((partitions[j].GetLastLBA() < UINT32_MAX) &&
-             (partitions[j].GetLastLBA() > UINT64_C(0))) {
-            do {
-               printf("Enter an MBR hex code (default %02X): ",
-                      typeHelper.GUIDToID(partitions[j].GetType()) / 256);
-               fgets(line, 255, stdin);
-               sscanf(line, "%x", &typeCode);
-               if (line[0] == '\n')
-                  typeCode = partitions[j].GetHexType() / 256;
-            } while ((typeCode <= 0) || (typeCode > 255));
-            printf("Set the bootable flag? ");
-            bootable = (GetYN() == 'Y');
-            length = partitions[j].GetLengthLBA();
-            if (eeFirst == 'Y')
-               mbrNum = i + 1;
-            else
-               mbrNum = i;
-            protectiveMBR.MakePart(mbrNum, (uint32_t) partitions[j].GetFirstLBA(),
-                                   (uint32_t) length, typeCode, bootable);
-            protectiveMBR.SetHybrid();
-         } else { // partition out of range
-            printf("Partition %d ends beyond the 2TiB limit of MBR partitions or does not exist;\n"
-                   "omitting it.\n",
-                   j + 1);
-         } // if/else
-      } else {
-         printf("Partition %d is out of range; omitting it.\n", j + 1);
-      } // if/else
+      if (eeFirst == 'Y')
+         mbrNum = i + 1;
+      else
+         mbrNum = i;
+      numConverted += OnePartToMBR(j, mbrNum);
    } // for
 
-   if (numParts > 0) { // User opted to create a hybrid MBR....
+   if ((numParts > 0) && (numConverted > 0)) { // User opted to create a hybrid MBR....
       // Create EFI protective partition that covers the start of the disk.
       // If this location (covering the main GPT data structures) is omitted,
       // Linux won't find any partitions on the disk. Note that this is
@@ -1544,6 +1635,7 @@
       else
          mbrNum = numParts;
       protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), 0xEE);
+      protectiveMBR.SetHybrid();
 
       // ... and for good measure, if there are any partition spaces left,
       // optionally create another protective EFI partition to cover as much
@@ -1692,7 +1784,7 @@
    // Now initialize a bunch of stuff that's static....
    mainHeader.signature = GPT_SIGNATURE;
    mainHeader.revision = 0x00010000;
-   mainHeader.headerSize = (uint32_t) HEADER_SIZE;
+   mainHeader.headerSize = HEADER_SIZE;
    mainHeader.reserved = 0;
    mainHeader.currentLBA = UINT64_C(1);
    mainHeader.partitionEntriesLBA = (uint64_t) 2;
@@ -1790,6 +1882,17 @@
    return numFound;
 } // GPTData::GetPartRange()
 
+// Returns the number of defined partitions.
+uint32_t GPTData::CountParts(void) {
+   int i, counted = 0;
+
+   for (i = 0; i < mainHeader.numParts; i++) {
+      if (partitions[i].GetFirstLBA() > 0)
+         counted++;
+   } // for
+   return counted;
+} // GPTData::CountParts()
+
 /****************************************************
  *                                                  *
  * Functions that return data about disk free space *
@@ -2017,8 +2120,8 @@
       fprintf(stderr, "MBRRecord is %d bytes, should be 16 bytes; aborting!\n", sizeof(MBRRecord));
       allOK = 0;
    } // if
-   if (sizeof(struct EBRRecord) != 512) {
-      fprintf(stderr, "EBRRecord is %d bytes, should be 512 bytes; aborting!\n", sizeof(EBRRecord));
+   if (sizeof(struct TempMBR) != 512) {
+      fprintf(stderr, "TempMBR is %d bytes, should be 512 bytes; aborting!\n", sizeof(TempMBR));
       allOK = 0;
    } // if
    if (sizeof(struct GPTHeader) != 512) {
diff --git a/gpt.h b/gpt.h
index 758f2fb..ea17e3d 100644
--- a/gpt.h
+++ b/gpt.h
@@ -67,7 +67,6 @@
    int secondPartsCrcOk;
    int apmFound; // set to 1 if APM detected
    int bsdFound; // set to 1 if BSD disklabel detected in MBR
-//   uint32_t units; // display units, in multiples of sectors
    PartTypes typeHelper;
 public:
    // Basic necessary functions....
@@ -87,6 +86,7 @@
    int FindOverlaps(void);
 
    // Load or save data from/to disk
+   int LoadMBR(char* f) {return protectiveMBR.ReadMBRData(f);}
    void PartitionScan(int fd);
    int LoadPartitions(char* deviceFilename);
    int ForceLoadGPTData(int fd);
@@ -111,13 +111,15 @@
    void DeletePartition(void);
    void ChangePartType(void);
    void SetAttributes(uint32_t partNum);
-   int DestroyGPT(void); // Returns 1 if user proceeds
+   int DestroyGPT(int prompt = 1); // Returns 1 if user proceeds
 
-   // Convert to GPT from other formats (may require user interaction)
+   // Convert between GPT and other formats (may require user interaction)
    WhichToUse UseWhichPartitions(void);
    int XFormPartitions(void);
    int XFormDisklabel(int OnGptPart = -1);
    int XFormDisklabel(BSDData* disklabel, int startPart);
+   int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful
+   int XFormToMBR(void); // convert GPT to MBR, wiping GPT afterwards. Returns 1 if successful
    void MakeHybrid(void);
 
    // Adjust GPT structures WITHOUT user interaction...
@@ -139,6 +141,8 @@
    uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;}
    uint64_t GetBlocksInPartTable(void) {return (mainHeader.numParts *
                    mainHeader.sizeOfPartitionEntries) / blockSize;}
+   uint32_t CountParts(void);
+
 
    // Find information about free space
    uint64_t FindFirstAvailable(uint64_t start = 0);
diff --git a/gptpart.cc b/gptpart.cc
index 671e42e..671d04e 100644
--- a/gptpart.cc
+++ b/gptpart.cc
@@ -9,8 +9,8 @@
 // Copyright: See COPYING file that comes with this distribution
 //
 //
-/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
-  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
 
 #define __STDC_LIMIT_MACROS
 #define __STDC_CONSTANT_MACROS
@@ -25,6 +25,10 @@
 PartTypes GPTPart::typeHelper;
 
 GPTPart::GPTPart(void) {
+   int i;
+
+   for (i = 0; i < NAME_SIZE; i++)
+      name[i] = '\0';
 } // Default constructor
 
 GPTPart::~GPTPart(void) {
@@ -128,16 +132,16 @@
 } // GPTPart::ReverseBytes()
 
 // Display summary information; does nothing if the partition is empty.
-void GPTPart::ShowSummary(int i, uint32_t blockSize, char* sizeInSI) {
-   int j;
+void GPTPart::ShowSummary(int partNum, uint32_t blockSize) {
+   char sizeInSI[255];
+   int j = 0;
 
    if (firstLBA != 0) {
       BytesToSI(blockSize * (lastLBA - firstLBA + 1), sizeInSI);
-      printf("%4d  %14lu  %14lu", i + 1, (unsigned long) firstLBA,
+      printf("%4d  %14lu  %14lu", partNum + 1, (unsigned long) firstLBA,
              (unsigned long) lastLBA);
       printf("   %-10s  %04X  ", sizeInSI,
              typeHelper.GUIDToID(partitionType));
-      j = 0;
       while ((name[j] != '\0') && (j < 44)) {
          printf("%c", name[j]);
          j += 2;
diff --git a/gptpart.h b/gptpart.h
index 6b834ca..8d396df 100644
--- a/gptpart.h
+++ b/gptpart.h
@@ -9,8 +9,8 @@
 // Copyright: See COPYING file that comes with this distribution
 //
 //
-/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
-  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
 
 #ifndef __GPTPART_H
 #define __GPTPART_H
@@ -23,11 +23,11 @@
 
 using namespace std;
 
-/*****************************************
- *                                       *
- * GUIDPart class and related structures *
- *                                       *
- *****************************************/
+/****************************************
+ *                                      *
+ * GPTPart class and related structures *
+ *                                      *
+ ****************************************/
 
 class GPTPart {
    protected:
@@ -73,7 +73,7 @@
 
       // Additional functions
       GPTPart & operator=(const GPTPart & orig);
-      void ShowSummary(int i, uint32_t blockSize, char* sizeInSI); // display summary information (1-line)
+      void ShowSummary(int partNum, uint32_t blockSize); // display summary information (1-line)
       void ShowDetails(uint32_t blockSize); // display detailed information (multi-line)
       void BlankPartition(void); // empty partition of data
       int DoTheyOverlap(GPTPart* other); // returns 1 if there's overlap
diff --git a/mbr.cc b/mbr.cc
index 4e789e1..2be731b 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -1,7 +1,7 @@
 /* mbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
    data. */
 
-/* By Rod Smith, January to February, 2009 */
+/* Initial coding by Rod Smith, January to February, 2009 */
 
 /* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
   under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
@@ -35,6 +35,8 @@
    strcpy(device, "");
    state = invalid;
    srand((unsigned int) time(NULL));
+   numHeads = MAX_HEADS;
+   numSecspTrack = MAX_SECSPERTRACK;
    EmptyMBR();
 } // MBRData default constructor
 
@@ -43,6 +45,8 @@
    diskSize = 0;
    strcpy(device, filename);
    state = invalid;
+   numHeads = MAX_HEADS;
+   numSecspTrack = MAX_SECSPERTRACK;
 
    srand((unsigned int) time(NULL));
    // Try to read the specified partition table, but if it fails....
@@ -55,51 +59,11 @@
 MBRData::~MBRData(void) {
 } // MBRData destructor
 
-// Empty all data. Meant mainly for calling by constructors, but it's also
-// used by the hybrid MBR functions in the GPTData class.
-void MBRData::EmptyMBR(int clearBootloader) {
-   int i;
-
-   // Zero out the boot loader section, the disk signature, and the
-   // 2-byte nulls area only if requested to do so. (This is the
-   // default.)
-   if (clearBootloader == 1) {
-      for (i = 0; i < 440; i++)
-         code[i] = 0;
-      diskSignature = (uint32_t) rand();
-      nulls = 0;
-   } // if
-
-   // Blank out the partitions
-   for (i = 0; i < 4; i++) {
-      partitions[i].status = UINT8_C(0);
-      partitions[i].firstSector[0] = UINT8_C(0);
-      partitions[i].firstSector[1] = UINT8_C(0);
-      partitions[i].firstSector[2] = UINT8_C(0);
-      partitions[i].partitionType = UINT8_C(0);
-      partitions[i].lastSector[0] = UINT8_C(0);
-      partitions[i].lastSector[1] = UINT8_C(0);
-      partitions[i].lastSector[2] = UINT8_C(0);
-      partitions[i].firstLBA = UINT32_C(0);
-      partitions[i].lengthLBA = UINT32_C(0);
-   } // for
-   MBRSignature = MBR_SIGNATURE;
-
-   blockSize = SECTOR_SIZE;
-   diskSize = 0;
-   for (i = 0; i < NUM_LOGICALS; i++) {
-      logicals[i].status = UINT8_C(0);
-      logicals[i].firstSector[0] = UINT8_C(0);
-      logicals[i].firstSector[1] = UINT8_C(0);
-      logicals[i].firstSector[2] = UINT8_C(0);
-      logicals[i].partitionType = UINT8_C(0);
-      logicals[i].lastSector[0] = UINT8_C(0);
-      logicals[i].lastSector[1] = UINT8_C(0);
-      logicals[i].lastSector[2] = UINT8_C(0);
-      logicals[i].firstLBA = UINT32_C(0);
-      logicals[i].lengthLBA = UINT32_C(0);
-   } // for
-} // MBRData::EmptyMBR()
+/**********************
+ *                    *
+ * Disk I/O functions *
+ *                    *
+ **********************/
 
 // Read data from MBR. Returns 1 if read was successful (even if the
 // data isn't a valid MBR), 0 if the read failed.
@@ -120,25 +84,18 @@
    return allOK;
 } // MBRData::ReadMBRData(char* deviceFilename)
 
-// Read data from MBR.
+// Read data from MBR. If checkBlockSize == 1 (the default), the block
+// size is checked; otherwise it's set to the default (512 bytes).
+// Note that any extended partition(s) present will be explicitly stored
+// in the partitions[] array, along with their contained partitions; the
+// extended container partition(s) should be ignored by other functions.
 void MBRData::ReadMBRData(int fd, int checkBlockSize) {
-   int allOK = 1, i, j, maxLogicals = 0;
+   int allOK = 1, i, j, logicalNum;
    int err;
    TempMBR tempMBR;
 
-   // Clear logical partition array
-   for (i = 0; i < NUM_LOGICALS; i++) {
-      logicals[i].status = UINT8_C(0);
-      logicals[i].firstSector[0] = UINT8_C(0);
-      logicals[i].firstSector[1] = UINT8_C(0);
-      logicals[i].firstSector[2] = UINT8_C(0);
-      logicals[i].partitionType = UINT8_C(0);
-      logicals[i].lastSector[0] = UINT8_C(0);
-      logicals[i].lastSector[1] = UINT8_C(0);
-      logicals[i].lastSector[2] = UINT8_C(0);
-      logicals[i].firstLBA = UINT32_C(0);
-      logicals[i].lengthLBA = UINT32_C(0);
-   } // for
+   // Empty existing MBR data, including the logical partitions...
+   EmptyMBR(0);
 
    err = lseek64(fd, 0, SEEK_SET);
    err = read(fd, &tempMBR, 512);
@@ -154,8 +111,8 @@
       for (j = 0; j < 3; j++) {
          partitions[i].firstSector[j] = tempMBR.partitions[i].firstSector[j];
          partitions[i].lastSector[j] = tempMBR.partitions[i].lastSector[j];
-      } // for j...
-   } // for i...
+      } // for j... (reading parts of CHS geometry)
+   } // for i... (reading all four partitions)
    MBRSignature = tempMBR.MBRSignature;
 
    // Reverse the byte order, if necessary
@@ -180,21 +137,16 @@
    // Find block size
    if (checkBlockSize) {
       blockSize = GetBlockSize(fd);
-//      if ((blockSize = GetBlockSize(fd)) == -1) {
-//         blockSize = SECTOR_SIZE;
-//         printf("Unable to determine sector size; assuming %lu bytes!\n",
-//               (unsigned long) SECTOR_SIZE);
-//      } // if
    } // if (checkBlockSize)
 
    // Load logical partition data, if any is found....
    if (allOK) {
       for (i = 0; i < 4; i++) {
          if ((partitions[i].partitionType == 0x05) || (partitions[i].partitionType == 0x0f)
-             || (partitions[i].partitionType == 0x85)) {
+              || (partitions[i].partitionType == 0x85)) {
             // Found it, so call a recursive algorithm to load everything from them....
-            maxLogicals = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), maxLogicals);
-            if ((maxLogicals < 0) || (maxLogicals > NUM_LOGICALS)) {
+            logicalNum = ReadLogicalPart(fd, partitions[i].firstLBA, UINT32_C(0), 4);
+            if ((logicalNum < 0) || (logicalNum >= MAX_MBR_PARTS)) {
                allOK = 0;
                fprintf(stderr, "Error reading logical partitions! List may be truncated!\n");
             } // if maxLogicals valid
@@ -224,26 +176,70 @@
              (partitions[i].partitionType != UINT8_C(0x00)))
             state = hybrid;
       } // for
-   } // if hybrid
-
-/*   // Tell the user what the MBR state is...
-   switch (state) {
-      case invalid:
-         printf("Information: MBR appears to be empty or invalid.\n");
-	 break;
-      case gpt:
-         printf("Information: MBR holds GPT placeholder partitions.\n");
-         break;
-      case hybrid:
-         printf("Information: MBR holds hybrid GPT/MBR data.\n");
-         break;
-      case mbr:
-         printf("Information: MBR data appears to be valid.\n");
-         break;
-   } // switch */
+   } // if (hybrid detection code)
 } // MBRData::ReadMBRData(int fd)
 
-// Write the MBR data to the default defined device.
+// This is a recursive function to read all the logical partitions, following the
+// logical partition linked list from the disk and storing the basic data in the
+// partitions[] array. Returns last index to partitions[] used, or -1 if there was
+// a problem.
+// Parameters:
+// fd = file descriptor
+// extendedStart = LBA of the start of the extended partition
+// diskOffset = LBA offset WITHIN the extended partition of the one to be read
+// partNum = location in partitions[] array to store retrieved data
+int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
+                             uint32_t diskOffset, int partNum) {
+   struct TempMBR ebr;
+   off_t offset;
+
+   // Check for a valid partition number. Note that partitions MAY be read into
+   // the area normally used by primary partitions, although the only calling
+   // function as of GPT fdisk version 0.5.0 doesn't do so.
+   if ((partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
+      offset = (off_t) (extendedStart + diskOffset) * blockSize;
+      if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
+         fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
+         partNum = -1;
+      }
+      if (read(fd, &ebr, 512) != 512) { // Load the data....
+         fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
+                 (unsigned long) offset);
+         partNum = -1;
+      } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
+         ReverseBytes(&ebr.MBRSignature, 2);
+         ReverseBytes(&ebr.partitions[0].firstLBA, 4);
+         ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
+         ReverseBytes(&ebr.partitions[1].firstLBA, 4);
+         ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
+      } // if/else/if
+
+      if (ebr.MBRSignature != MBR_SIGNATURE) {
+         partNum = -1;
+         fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
+                (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
+      } // if
+
+      // Copy over the basic data....
+      partitions[partNum].status = ebr.partitions[0].status;
+      partitions[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
+      partitions[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
+      partitions[partNum].partitionType = ebr.partitions[0].partitionType;
+
+      // Find the next partition (if there is one) and recurse....
+      if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 4) &&
+          (partNum < (MAX_MBR_PARTS - 1))) {
+         partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
+                                   partNum + 1);
+      } else {
+         partNum++;
+      } // if another partition
+   } // Not enough space for all the logicals (or previous error encountered)
+   return (partNum);
+} // MBRData::ReadLogicalPart()
+
+// Write the MBR data to the default defined device. Note that this writes
+// ONLY the MBR itself, not the logical partition data.
 int MBRData::WriteMBRData(void) {
    int allOK = 1, fd;
 
@@ -295,12 +291,6 @@
    lseek64(fd, 0, SEEK_SET);
    write(fd, &tempMBR, 512);
 
-/*   write(fd, code, 440);
-   write(fd, &diskSignature, 4);
-   write(fd, &nulls, 2);
-   write(fd, partitions, 64);
-   write(fd, &MBRSignature, 2); */
-
    // Reverse the byte order back, if necessary
    if (IsLittleEndian() == 0) {
       ReverseBytes(&diskSignature, 4);
@@ -313,56 +303,11 @@
    }// if
 } // MBRData::WriteMBRData(int fd)
 
-// This is a recursive function to read all the logical partitions, following the
-// logical partition linked list from the disk and storing the basic data in the
-// logicals[] array. Returns last index to logicals[] uses, or -1 if there was a
-// problem
-int MBRData::ReadLogicalPart(int fd, uint32_t extendedStart,
-                             uint32_t diskOffset, int partNum) {
-   struct EBRRecord ebr;
-   off_t offset;
-
-   if ((partNum < NUM_LOGICALS) && (partNum >= 0)) {
-      offset = (off_t) (extendedStart + diskOffset) * blockSize;
-      if (lseek64(fd, offset, SEEK_SET) == (off_t) -1) { // seek to EBR record
-         fprintf(stderr, "Unable to seek to %lu! Aborting!\n", (unsigned long) offset);
-         partNum = -1;
-      }
-      if (read(fd, &ebr, 512) != 512) { // Load the data....
-         fprintf(stderr, "Error seeking to or reading logical partition data from %lu!\nAborting!\n",
-                 (unsigned long) offset);
-         partNum = -1;
-      } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
-         ReverseBytes(&ebr.MBRSignature, 2);
-         ReverseBytes(&ebr.partitions[0].firstLBA, 4);
-         ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
-         ReverseBytes(&ebr.partitions[1].firstLBA, 4);
-         ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
-      } // if/else/if
-
-      if (ebr.MBRSignature != MBR_SIGNATURE) {
-         partNum = -1;
-         fprintf(stderr, "MBR signature in logical partition invalid; read 0x%04X, but should be 0x%04X\n",
-                (unsigned int) ebr.MBRSignature, (unsigned int) MBR_SIGNATURE);
-      } // if
-
-      // Copy over the basic data....
-      logicals[partNum].status = ebr.partitions[0].status;
-      logicals[partNum].firstLBA = ebr.partitions[0].firstLBA + diskOffset + extendedStart;
-      logicals[partNum].lengthLBA = ebr.partitions[0].lengthLBA;
-      logicals[partNum].partitionType = ebr.partitions[0].partitionType;
-
-      // Find the next partition (if there is one) and recurse....
-      if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum >= 0) &&
-          (partNum < (NUM_LOGICALS - 1))) {
-         partNum = ReadLogicalPart(fd, extendedStart, ebr.partitions[1].firstLBA,
-                                   partNum + 1);
-         } else {
-            partNum++;
-      } // if another partition
-   } // Not enough space for all the logicals (or previous error encountered)
-   return (partNum);
-} // MBRData::ReadLogicalPart()
+/********************************************
+ *                                          *
+ * Functions that display data for the user *
+ *                                          *
+ ********************************************/
 
 // Show the MBR data to the user....
 void MBRData::DisplayMBRData(void) {
@@ -373,10 +318,10 @@
    printf("MBR disk identifier: 0x%08X\n", (unsigned int) diskSignature);
    printf("MBR partitions:\n");
    printf("Number\t Boot\t Start (sector)\t Length (sectors)\tType\n");
-   for (i = 0; i < 4; i++) {
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
       if (partitions[i].lengthLBA != 0) {
          if (partitions[i].status && 0x80) // it's bootable
-	    bootCode = '*';
+            bootCode = '*';
          else
             bootCode = ' ';
          printf("%4d\t   %c\t%13lu\t%15lu \t0x%02X\n", i + 1, bootCode,
@@ -384,31 +329,144 @@
                 (unsigned long) partitions[i].lengthLBA, partitions[i].partitionType);
       } // if
    } // for
-
-   // Now display logical partition data....
-   for (i = 0; i < NUM_LOGICALS; i++) {
-      if (logicals[i].lengthLBA != 0) {
-         printf("%4d\t%13lu\t%15lu \t0x%02X\n", i + 5, (unsigned long) logicals[i].firstLBA,
-                (unsigned long) logicals[i].lengthLBA, logicals[i].partitionType);
-      } // if
-   } // for
-   printf("\nDisk size is %lu sectors (%s)\n", (unsigned long) diskSize,
+   printf("\nDisk size is %llu sectors (%s)\n", (unsigned long long) diskSize,
           BytesToSI(diskSize * (uint64_t) blockSize, tempStr));
 } // MBRData::DisplayMBRData()
 
+// Displays the state, as a word, on stdout. Used for debugging & to
+// tell the user about the MBR state when the program launches....
+void MBRData::ShowState(void) {
+   switch (state) {
+      case invalid:
+         printf("  MBR: not present\n");
+         break;
+      case gpt:
+         printf("  MBR: protective\n");
+         break;
+      case hybrid:
+         printf("  MBR: hybrid\n");
+         break;
+      case mbr:
+         printf("  MBR: MBR only\n");
+         break;
+      default:
+         printf("\a  MBR: unknown -- bug!\n");
+         break;
+   } // switch
+} // MBRData::ShowState()
+
+/*********************************************************************
+ *                                                                   *
+ * Functions that set or get disk metadata (CHS geometry, disk size, *
+ * etc.)                                                             *
+ *                                                                   *
+ *********************************************************************/
+
+// Sets the CHS geometry. CHS geometry is used by LBAtoCHS() function.
+// Note that this only sets the heads and sectors; the number of
+// cylinders is determined by these values and the disk size.
+void MBRData::SetCHSGeom(uint32_t h, uint32_t s) {
+   if ((h <= MAX_HEADS) && (s <= MAX_SECSPERTRACK)) {
+      numHeads = h;
+      numSecspTrack = s;
+   } else {
+      printf("Warning! Attempt to set invalid CHS geometry!\n");
+   } // if/else
+} // MBRData::SetCHSGeom()
+
+// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
+// was within the range that can be expressed by CHS (including 0, for an
+// empty partition), 0 if the value is outside that range, and -1 if chs is
+// invalid.
+int MBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
+   uint64_t cylinder, head, sector; // all numbered from 0
+   uint64_t remainder;
+   int retval = 1;
+   int done = 0;
+
+   if (chs != NULL) {
+      // Special case: In case of 0 LBA value, zero out CHS values....
+      if (lba == 0) {
+         chs[0] = chs[1] = chs[2] = UINT8_C(0);
+         done = 1;
+      } // if
+      // If LBA value is too large for CHS, max out CHS values....
+      if ((!done) && (lba >= (numHeads * numSecspTrack * MAX_CYLINDERS))) {
+         chs[0] = 254;
+         chs[1] = chs[2] = 255;
+         done = 1;
+         retval = 0;
+      } // if
+      // If neither of the above applies, compute CHS values....
+      if (!done) {
+         cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
+         remainder = lba - (cylinder * numHeads * numSecspTrack);
+         head = remainder / numSecspTrack;
+         remainder -= head * numSecspTrack;
+         sector = remainder;
+         if (head < numHeads)
+            chs[0] = head;
+         else
+            retval = 0;
+         if (sector < numSecspTrack) {
+            chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
+            chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
+         } else {
+            retval = 0;
+         } // if/else
+      } // if value is expressible and non-0
+   } else { // Invalid (NULL) chs pointer
+      retval = -1;
+   } // if CHS pointer valid
+   return (retval);
+} // MBRData::LBAtoCHS()
+
+/*****************************************************
+ *                                                   *
+ * Functions to create, delete, or change partitions *
+ *                                                   *
+ *****************************************************/
+
+// Empty all data. Meant mainly for calling by constructors, but it's also
+// used by the hybrid MBR functions in the GPTData class.
+void MBRData::EmptyMBR(int clearBootloader) {
+   int i;
+
+   // Zero out the boot loader section, the disk signature, and the
+   // 2-byte nulls area only if requested to do so. (This is the
+   // default.)
+   if (clearBootloader == 1) {
+      for (i = 0; i < 440; i++)
+         code[i] = 0;
+      diskSignature = (uint32_t) rand();
+      nulls = 0;
+   } // if
+
+   // Blank out the partitions
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      partitions[i].status = UINT8_C(0);
+      partitions[i].firstSector[0] = UINT8_C(0);
+      partitions[i].firstSector[1] = UINT8_C(0);
+      partitions[i].firstSector[2] = UINT8_C(0);
+      partitions[i].partitionType = UINT8_C(0);
+      partitions[i].lastSector[0] = UINT8_C(0);
+      partitions[i].lastSector[1] = UINT8_C(0);
+      partitions[i].lastSector[2] = UINT8_C(0);
+      partitions[i].firstLBA = UINT32_C(0);
+      partitions[i].lengthLBA = UINT32_C(0);
+   } // for
+   MBRSignature = MBR_SIGNATURE;
+} // MBRData::EmptyMBR()
+
 // Create a protective MBR. Clears the boot loader area if clearBoot > 0.
 void MBRData::MakeProtectiveMBR(int clearBoot) {
-   int i;
+
+   EmptyMBR(clearBoot);
 
    // Initialize variables
    nulls = 0;
    MBRSignature = MBR_SIGNATURE;
 
-   if (clearBoot > 0) {
-      for (i = 0; i < 440; i++)
-         code[i] = (uint8_t) 0;
-   } // if
-
    partitions[0].status = UINT8_C(0); // Flag the protective part. as unbootable
 
    // Write CHS data. This maxes out the use of the disk, as much as
@@ -427,44 +485,43 @@
    partitions[0].partitionType = UINT8_C(0xEE);
    partitions[0].firstLBA = UINT32_C(1);
    if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
-      partitions[0].lengthLBA = diskSize - 1;
+      partitions[0].lengthLBA = (uint32_t) diskSize - UINT32_C(1);
    } else { // disk is too big to represent, so fake it...
       partitions[0].lengthLBA = UINT32_MAX;
    } // if/else
 
-   // Zero out three unused primary partitions...
-   for (i = 1; i < 4; i++) {
-      partitions[i].status = UINT8_C(0);
-      partitions[i].firstSector[0] = UINT8_C(0);
-      partitions[i].firstSector[1] = UINT8_C(0);
-      partitions[i].firstSector[2] = UINT8_C(0);
-      partitions[i].partitionType = UINT8_C(0);
-      partitions[i].lastSector[0] = UINT8_C(0);
-      partitions[i].lastSector[1] = UINT8_C(0);
-      partitions[i].lastSector[2] = UINT8_C(0);
-      partitions[i].firstLBA = UINT32_C(0);
-      partitions[i].lengthLBA = UINT32_C(0);
-   } // for
-
-   // Zero out all the logical partitions. Not necessary for data
-   // integrity on write, but eliminates stray entries if user wants
-   // to view the MBR after converting the disk
-   for (i = 0; i < NUM_LOGICALS; i++) {
-      logicals[i].status = UINT8_C(0);
-      logicals[i].firstSector[0] = UINT8_C(0);
-      logicals[i].firstSector[1] = UINT8_C(0);
-      logicals[i].firstSector[2] = UINT8_C(0);
-      logicals[i].partitionType = UINT8_C(0);
-      logicals[i].lastSector[0] = UINT8_C(0);
-      logicals[i].lastSector[1] = UINT8_C(0);
-      logicals[i].lastSector[2] = UINT8_C(0);
-      logicals[i].firstLBA = UINT32_C(0);
-      logicals[i].lengthLBA = UINT32_C(0);
-   } // for
-
    state = gpt;
 } // MBRData::MakeProtectiveMBR()
 
+// Create a partition of the specified number, starting LBA, and
+// length. This function does *NO* error checking, so it's possible
+// to seriously screw up a partition table using this function!
+// Note: This function should NOT be used to create the 0xEE partition
+// in a conventional GPT configuration, since that partition has
+// specific size requirements that this function won't handle. It may
+// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
+// since those toss the rulebook away anyhow....
+void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
+                       int bootable) {
+   if ((num >= 0) && (num < MAX_MBR_PARTS)) {
+      partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
+      partitions[num].firstSector[0] = UINT8_C(0);
+      partitions[num].firstSector[1] = UINT8_C(0);
+      partitions[num].firstSector[2] = UINT8_C(0);
+      partitions[num].partitionType = (uint8_t) type;
+      partitions[num].lastSector[0] = UINT8_C(0);
+      partitions[num].lastSector[1] = UINT8_C(0);
+      partitions[num].lastSector[2] = UINT8_C(0);
+      partitions[num].firstLBA = start;
+      partitions[num].lengthLBA = length;
+      // If this is a "real" partition, set its CHS geometry
+      if (length > 0) {
+         LBAtoCHS((uint64_t) start, partitions[num].firstSector);
+         LBAtoCHS((uint64_t) (start + length - 1), partitions[num].lastSector);
+      } // if (length > 0)
+   } // if valid partition number
+} // MBRData::MakePart()
+
 // Create a partition that fills the most available space. Returns
 // 1 if partition was created, 0 otherwise. Intended for use in
 // creating hybrid MBRs.
@@ -482,11 +539,11 @@
       if (firstBlock != UINT32_C(0)) { // something's free...
          lastBlock = FindLastInFree(firstBlock);
          segmentSize = lastBlock - firstBlock + UINT32_C(1);
-	 if (segmentSize > selectedSize) {
+         if (segmentSize > selectedSize) {
             selectedSize = segmentSize;
-	    selectedSegment = firstBlock;
-	 } // if
-	 start = lastBlock + 1;
+            selectedSegment = firstBlock;
+         } // if
+         start = lastBlock + 1;
       } // if
    } while (firstBlock != 0);
    if ((selectedSize > UINT32_C(0)) && ((uint64_t) selectedSize < diskSize)) {
@@ -517,18 +574,19 @@
 // Used to help keep GPT & hybrid MBR partitions in sync....
 int MBRData::DeleteByLocation(uint64_t start64, uint64_t length64) {
    uint32_t start32, length32;
-   int i, j, deleted = 0;
+   int i, deleted = 0;
 
-   if ((state == hybrid) && (start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
+   if ((start64 < UINT32_MAX) && (length64 < UINT32_MAX)) {
       start32 = (uint32_t) start64;
       length32 = (uint32_t) length64;
-      for (i = 0; i < 4; i++) {
+      for (i = 0; i < MAX_MBR_PARTS; i++) {
          if ((partitions[i].firstLBA == start32) && (partitions[i].lengthLBA = length32) &&
-             (partitions[i].partitionType != 0xEE)) {
+              (partitions[i].partitionType != 0xEE)) {
             DeletePartition(i);
-            OptimizeEESize();
+            if (state == hybrid)
+               OptimizeEESize();
             deleted = 1;
-         } // if (match found)
+              } // if (match found)
       } // for i (partition scan)
    } // if (hybrid & GPT partition < 2TiB)
    return deleted;
@@ -554,74 +612,24 @@
             partitions[i].lengthLBA = FindLastInFree(after) - partitions[i].firstLBA + 1;
          } // if free space after
       } // if partition is 0xEE
-      if (typeFlag == 0) { // No non-hybrid partitions found
-         MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
-      } // if
    } // for partition loop
+   if (typeFlag == 0) { // No non-hybrid partitions found
+      MakeProtectiveMBR(); // ensure it's a fully compliant hybrid MBR.
+   } // if
 } // MBRData::OptimizeEESize()
 
-// Return a pointer to a primary or logical partition, or NULL if
-// the partition is out of range....
-struct MBRRecord* MBRData::GetPartition(int i) {
-   MBRRecord* thePart = NULL;
-
-   if ((i >= 0) && (i < 4)) { // primary partition
-      thePart = &partitions[i];
-   } // if
-   if ((i >= 4) && (i < (NUM_LOGICALS + 4))) {
-      thePart = &logicals[i - 4];
-   } // if
-   return thePart;
-} // GetPartition()
-
-// Displays the state, as a word, on stdout. Used for debugging & to
-// tell the user about the MBR state when the program launches....
-void MBRData::ShowState(void) {
-   switch (state) {
-      case invalid:
-         printf("  MBR: not present\n");
-         break;
-      case gpt:
-         printf("  MBR: protective\n");
-         break;
-      case hybrid:
-         printf("  MBR: hybrid\n");
-         break;
-      case mbr:
-         printf("  MBR: MBR only\n");
-         break;
-      default:
-         printf("\a  MBR: unknown -- bug!\n");
-         break;
-   } // switch
-} // MBRData::ShowState()
-
-// Create a primary partition of the specified number, starting LBA,
-// and length. This function does *NO* error checking, so it's possible
-// to seriously screw up a partition table using this function! It's
-// intended as a way to create a hybrid MBR, which is a pretty funky
-// setup to begin with....
-void MBRData::MakePart(int num, uint32_t start, uint32_t length, int type,
-                       int bootable) {
-
-   partitions[num].status = (uint8_t) bootable * (uint8_t) 0x80;
-   partitions[num].firstSector[0] = UINT8_C(0);
-   partitions[num].firstSector[1] = UINT8_C(0);
-   partitions[num].firstSector[2] = UINT8_C(0);
-   partitions[num].partitionType = (uint8_t) type;
-   partitions[num].lastSector[0] = UINT8_C(0);
-   partitions[num].lastSector[1] = UINT8_C(0);
-   partitions[num].lastSector[2] = UINT8_C(0);
-   partitions[num].firstLBA = start;
-   partitions[num].lengthLBA = length;
-} // MakePart()
+/****************************************
+ *                                      *
+ * Functions to find data on free space *
+ *                                      *
+ ****************************************/
 
 // Finds the first free space on the disk from start onward; returns 0
 // if none available....
 uint32_t MBRData::FindFirstAvailable(uint32_t start) {
    uint32_t first;
    uint32_t i;
-   int firstMoved = 0;
+   int firstMoved;
 
    first = start;
 
@@ -638,7 +646,7 @@
              (first < (partitions[i].firstLBA + partitions[i].lengthLBA))) {
             first = partitions[i].firstLBA + partitions[i].lengthLBA;
             firstMoved = 1;
-          } // if
+         } // if
       } // for
    } while (firstMoved == 1);
    if (first >= diskSize)
@@ -651,7 +659,7 @@
    uint32_t nearestStart;
    uint32_t i;
 
-   if (diskSize <= UINT32_MAX)
+   if ((diskSize <= UINT32_MAX) && (diskSize > 0))
       nearestStart = diskSize - 1;
    else
       nearestStart = UINT32_MAX - 1;
@@ -688,6 +696,8 @@
 
    for (i = 0; i < 4; i++) {
       first = partitions[i].firstLBA;
+      // Note: Weird two-line thing to avoid subtracting 1 from a 0 value
+      // for an unsigned int....
       last = first + partitions[i].lengthLBA;
       if (last > 0) last--;
       if ((first <= sector) && (last >= sector))
@@ -696,6 +706,12 @@
    return isFree;
 } // MBRData::IsFree()
 
+/******************************************************
+ *                                                    *
+ * Functions that extract data on specific partitions *
+ *                                                    *
+ ******************************************************/
+
 uint8_t MBRData::GetStatus(int i) {
    MBRRecord* thePart;
    uint8_t retval;
@@ -729,7 +745,7 @@
       retval = thePart->firstLBA;
    } else
       retval = UINT32_C(0);
-   return retval;
+      return retval;
 } // MBRData::GetFirstSector()
 
 uint32_t MBRData::GetLength(int i) {
@@ -741,7 +757,7 @@
       retval = thePart->lengthLBA;
    } else
       retval = UINT32_C(0);
-   return retval;
+      return retval;
 } // MBRData::GetLength()
 
 // Return the MBR data as a GPT partition....
@@ -772,7 +788,23 @@
          newPart.SetUniqueGUID(1);
          newPart.SetAttributes(0);
          newPart.SetName((unsigned char*) newPart.GetNameType(tempStr));
-      } // if
-   } // if
+      } // if not extended, protective, or non-existent
+   } // if (origPart != NULL)
    return newPart;
 } // MBRData::AsGPT()
+
+/***********************
+ *                     *
+ * Protected functions *
+ *                     *
+ ***********************/
+
+// Return a pointer to a primary or logical partition, or NULL if
+// the partition is out of range....
+struct MBRRecord* MBRData::GetPartition(int i) {
+   MBRRecord* thePart = NULL;
+
+   if ((i >= 0) && (i < MAX_MBR_PARTS))
+      thePart = &partitions[i];
+   return thePart;
+} // GetPartition()
diff --git a/mbr.h b/mbr.h
index ff824c5..8f7dfbc 100644
--- a/mbr.h
+++ b/mbr.h
@@ -12,9 +12,12 @@
 #define __MBRSTRUCTS
 
 #define MBR_SIGNATURE UINT16_C(0xAA55)
+#define MAX_HEADS 255        /* numbered 0 - 254 */
+#define MAX_SECSPERTRACK 63  /* numbered 1 - 63 */
+#define MAX_CYLINDERS 1024   /* numbered 0 - 1023 */
 
-// Maximum number of logical partitions supported
-#define NUM_LOGICALS 124
+// Maximum number of MBR partitions
+#define MAX_MBR_PARTS 128
 
 using namespace std;
 
@@ -36,9 +39,10 @@
    uint32_t lengthLBA;
 }; // struct MBRRecord
 
-// Create a 512-byte data structure into which the MBR can be loaded in one
+// A 512-byte data structure into which the MBR can be loaded in one
 // go, for the benefit of FreeBSD which seems to flake out when loading
-// from block devices in multiples other than the block size....
+// from block devices in multiples other than the block size.
+// Also used when loading logical partitions.
 struct TempMBR {
    uint8_t code[440];
    uint32_t diskSignature;
@@ -47,20 +51,6 @@
    uint16_t MBRSignature;
 }; // struct TempMBR
 
-// Extended Boot Record (EBR) data, used to hold one logical partition's
-// data within an extended partition. Includes pointer to next record for
-// in-memory linked-list access. This is similar to MBRData, but with a
-// few tweaks....
-struct EBRRecord {
-   uint8_t code[446]; // generally 0s (and we don't care if they aren't)
-   // First partition entry defines partition; second points to next
-   // entry in on-disk linked list; remaining two are unused. Note that
-   // addresses are relative to the extended partition, not to the disk
-   // as a whole.
-   struct MBRRecord partitions[4];
-   uint16_t MBRSignature;
-}; // struct EBRRecord
-
 // Possible states of the MBR
 enum MBRValidity {invalid, gpt, hybrid, mbr};
 
@@ -70,45 +60,55 @@
    uint8_t code[440];
    uint32_t diskSignature;
    uint16_t nulls;
-   struct MBRRecord partitions[4];
+   // MAX_MBR_PARTS defaults to 128. This array holds both the primary and
+   // the logical partitions, to simplify data retrieval for GPT conversions.
+   struct MBRRecord partitions[MAX_MBR_PARTS];
    uint16_t MBRSignature;
 
    // Above are basic MBR data; now add more stuff....
    uint32_t blockSize; // block size (usually 512)
    uint64_t diskSize; // size in blocks
+   uint64_t numHeads; // number of heads, in CHS scheme
+   uint64_t numSecspTrack; // number of sectors per track, in CHS scheme
    char device[256];
-   // Now an array of partitions for the logicals, in array form (easier
-   // than a linked list, and good enough for the GPT itself, so....)
-   struct MBRRecord logicals[NUM_LOGICALS];
    MBRValidity state;
    struct MBRRecord* GetPartition(int i); // Return primary or logical partition
 public:
    MBRData(void);
    MBRData(char* deviceFilename);
    ~MBRData(void);
-   // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
-   void EmptyMBR(int clearBootloader = 1);
-   void SetDiskSize(uint64_t ds) {diskSize = ds;}
+
+   // File I/O functions...
    int ReadMBRData(char* deviceFilename);
    void ReadMBRData(int fd, int checkBlockSize = 1);
+   int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
+                       int partNum);
    int WriteMBRData(void);
    void WriteMBRData(int fd);
    // ReadLogicalPart() returns last partition # read to logicals[] array,
    // or -1 if there was a problem....
-   int ReadLogicalPart(int fd, uint32_t extendedStart, uint32_t diskOffset,
-                       int partNum);
+
+   // Display data for user...
    void DisplayMBRData(void);
-   void MakeProtectiveMBR(int clearBoot = 0);
-   MBRValidity GetValidity(void) {return state;}
-   void ShowValidity(void);
    void ShowState(void);
+
+   // Functions that set or get disk metadata (size, CHS geometry, etc.)
+   void SetDiskSize(uint64_t ds) {diskSize = ds;}
+   MBRValidity GetValidity(void) {return state;}
+   void SetHybrid(void) {state = hybrid;} // Set hybrid flag
+   void SetCHSGeom(uint32_t h, uint32_t s);
+   int LBAtoCHS(uint64_t lba, uint8_t * chs); // Convert LBA to CHS
+
+   // Functions to create, delete, or change partitions
+   // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
+   void EmptyMBR(int clearBootloader = 1);
+   void MakeProtectiveMBR(int clearBoot = 0);
    void MakePart(int num, uint32_t startLBA, uint32_t lengthLBA, int type = 0x07,
                  int bootable = 0);
    int MakeBiggestPart(int i, int type); // Make partition filling most space
    void DeletePartition(int i);
    int DeleteByLocation(uint64_t start64, uint64_t length64);
    void OptimizeEESize(void);
-   void SetHybrid(void) {state = hybrid;} // Set hybrid flag
 
    // Functions to find information on free space....
    uint32_t FindFirstAvailable(uint32_t start = 1);
diff --git a/parttypes.cc b/parttypes.cc
index 5cdac0b..20289c6 100644
--- a/parttypes.cc
+++ b/parttypes.cc
@@ -179,7 +179,7 @@
               "EFI System"); // EFI System (parted marks Linux boot
                              // partitions like this)
       AddType(0xEF01, UINT64_C(0x11d333e7024dee41), UINT64_C(0x9FF381C70800699d),
-              "MBR partition scheme"); // Whatever that is (from Wikipedia)
+              "MBR partition scheme"); // Used to nest an MBR table on a GPT disk
       AddType(0xEF02, UINT64_C(0x6E6F644921686148), UINT64_C(0x4946456465654E74),
               "BIOS boot partition"); //
 
diff --git a/support.cc b/support.cc
index e5b1860..337dfce 100644
--- a/support.cc
+++ b/support.cc
@@ -205,7 +205,9 @@
       result = SECTOR_SIZE;
       // ENOTTY = inappropriate ioctl; probably being called on a disk image
       // file, so don't display the warning message....
-      if (errno != ENOTTY) {
+      // 32-bit code returns EINVAL, I don't know why. I know I'm treading on
+      // thin ice here, but it should be OK in all but very weird cases....
+      if ((errno != ENOTTY) && (errno != EINVAL)) {
          printf("\aError %d when determining sector size! Setting sector size to %d\n",
                 errno, SECTOR_SIZE);
       } // if
@@ -434,8 +436,9 @@
 uint64_t disksize(int fd, int *err) {
    long sz; // Do not delete; needed for Linux
    long long b; // Do not delete; needed for Linux
-   uint64_t sectors = 0, bytes = 0; // size in sectors & bytes
-   struct stat st;
+   uint64_t sectors = 0; // size in sectors
+   off_t bytes = 0; // size in bytes
+   struct stat64 st;
 
    // Note to self: I recall testing a simplified version of
    // this code, similar to what's in the __APPLE__ block,
@@ -462,17 +465,6 @@
          sectors = (b >> 9);
    } // if
 
-//         if (*err) {
-//            sz = 0;
-//            if (errno != EFBIG)
-//               return sz;
-//         }
-//         *err = ioctl(fd, BLKGETSIZE64, &b);
-//         if (*err || b == 0 || b == sz)
-//            sectors = sz;
-//         else
-//            sectors = (b >> 9);
-
 #endif
 #endif
 
@@ -480,7 +472,7 @@
    // so let's assume it's a regular file (a QEMU image, dd backup, or
    // what have you) and see what stat() gives us....
    if (sectors == 0) {
-      if (fstat(fd, &st) == 0) {
+      if (fstat64(fd, &st) == 0) {
          bytes = (uint64_t) st.st_size;
          if ((bytes % UINT64_C(512)) != 0)
             fprintf(stderr, "Warning: File size is not a multiple of 512 bytes!"
diff --git a/support.h b/support.h
index d6e4f92..8551e17 100644
--- a/support.h
+++ b/support.h
@@ -4,6 +4,10 @@
 #include <stdint.h>
 #include <unistd.h>
 #include <stdlib.h>
+#include <string.h>
+
+#ifndef __GPTSUPPORT
+#define __GPTSUPPORT
 
 #if defined (__FreeBSD__) || defined (__APPLE__)
 // Darwin (Mac OS) only: disk IOCTLs are different, and there is no lseek64
@@ -18,10 +22,10 @@
 #include <linux/fs.h>
 #endif
 
-#include <string.h>
-
-#ifndef __GPTSUPPORT
-#define __GPTSUPPORT
+#ifdef __FreeBSD__
+#define fstat64 fstat
+#define stat64 stat
+#endif
 
 // Set this as a default
 #define SECTOR_SIZE UINT32_C(512)
@@ -39,7 +43,7 @@
 // Number and size of GPT entries...
 #define NUM_GPT_ENTRIES 128
 #define GPT_SIZE 128
-#define HEADER_SIZE 92
+#define HEADER_SIZE UINT32_C(92)
 #define GPT_RESERVED 420
 #define NAME_SIZE 72