Updated project files for 0.6.5 release version.
diff --git a/CHANGELOG b/CHANGELOG
index 1bf0efe..2d7b600 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -1,4 +1,53 @@
-0.6.4.1 (2/20/2010):
+0.6.5 (3/7/2010):
+-----------------
+
+- Added tests to verify ('v') function and to pre-save checks to look for
+  partitions that end before they begin or that are too big for their
+  disks.
+
+- Fixed a bug that could cause spurious data to appear in a grown partition
+  table.
+
+- Added ability to convert some or all partitions to logical partitions in
+  GPT-to-MBR conversion. This feature is limited by the fact that at least
+  one free sector must exist immediately prior to each logical partition,
+  so it won't do much good if partitions are crammed together. It should be
+  possible to convert back to MBR any disk that started that way, provided
+  no partitions were added or resized when the disk was in GPT form; and
+  disks that were partitioned with Apple's Disk Utility or other tools that
+  insert unpartitioned space should also be convertible. CAUTION: THE
+  LOGICAL PARTITION CREATION FEATURE DOESN'T TRY TO ALIGN PARTITIONS OR
+  PARTITION HEADER DATA TO CYLINDER BOUNDARIES! It's conceivable that some
+  older OSes or utilities will object to these disks, although Linux, OS X,
+  Windows Vista, and Windows 7 all seem happy with them.
+
+- Fixed bug that caused creation of 0-length file if an incorrect device
+  filename was typed.
+
+- The gdisk program now prompts for a device filename if it's called with
+  no options. This enables gdisk to do something useful if it's launched by
+  double-clicking its icon in a GUI environment.
+
+- Added workaround for bug in some versions of MinGW that caused the
+  program to garble input sector numbers.
+
+- The Windows version now works on disks with over-512-byte sectors.
+  Tested on a magneto-optical (MO) drive with 2048-byte sectors.
+
+- Added -D (--display-alignment) option to sgdisk, to display sector
+  alignment value (by default, 1 for sub-800GiB disks and 8 for disks
+  over that size).
+
+- Fixed bug in computation of CHS geometries for protective MBR. This is
+  non-critical, since most modern utilities ignore the CHS geometries.
+  Concerned users can use the 'n' option on the experts' menu to build new
+  protective MBRs with the new algorithm, if desired. (Note that GNU
+  Parted, at least, gets this wrong, too.)
+
+- Fixed memory-allocation bug when reading GPT disks with partition tables
+  with over 128 entries; could cause program to crash on startup.
+
+0.6.4-2 (2/20/2010):
 --------------------
 
 Note: Neither of the following changes affects actual program code, so I've
diff --git a/Makefile b/Makefile
index eda62fe..5b488ed 100644
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 CFLAGS+=-D_FILE_OFFSET_BITS=64
 CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64
 LDFLAGS+=
-LIB_NAMES=crc32 support guid gptpart mbr gpt bsd parttypes attributes diskio diskio-unix
+LIB_NAMES=crc32 support guid partnotes gptpart mbr gpt bsd parttypes attributes diskio diskio-unix
 LIB_SRCS=$(NAMES:=.cc)
 LIB_OBJS=$(LIB_NAMES:=.o)
 LIB_HEADERS=$(LIB_NAMES:=.h)
diff --git a/Makefile.mac b/Makefile.mac
index 950da20..99a6ea3 100644
--- a/Makefile.mac
+++ b/Makefile.mac
@@ -2,7 +2,7 @@
 CXX=g++
 CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g
 CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g
-LIB_NAMES=crc32 support guid gptpart mbr gpt bsd parttypes attributes diskio diskio-unix
+LIB_NAMES=crc32 support guid partnotes gptpart mbr gpt bsd parttypes attributes diskio diskio-unix
 LIB_SRCS=$(NAMES:=.cc)
 LIB_OBJS=$(LIB_NAMES:=.o)
 LIB_HEADERS=$(LIB_NAMES:=.h)
diff --git a/Makefile.mingw b/Makefile.mingw
index f6d0ba8..07e2de7 100644
--- a/Makefile.mingw
+++ b/Makefile.mingw
@@ -1,10 +1,10 @@
-CC=/usr/bin/i686-pc-mingw32-gcc
-CXX=/usr/bin/i686-pc-mingw32-g++
-STRIP=/usr/bin/i686-pc-mingw32-strip
+CC=/usr/bin/i586-mingw32msvc-gcc
+CXX=/usr/bin/i586-mingw32msvc-g++
+STRIP=/usr/bin/i586-mingw32msvc-strip
 CFLAGS=-O2 -D_FILE_OFFSET_BITS=64 -g
 CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -g
 #CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include -I/opt/local/include -g
-LIB_NAMES=guid gptpart bsd parttypes attributes crc32 mbr gpt support diskio diskio-windows
+LIB_NAMES=guid gptpart partnotes bsd parttypes attributes crc32 mbr gpt support diskio diskio-windows
 LIB_SRCS=$(NAMES:=.cc)
 LIB_OBJS=$(LIB_NAMES:=.o)
 LIB_HEADERS=$(LIB_NAMES:=.h)
@@ -12,8 +12,8 @@
 
 all:	gdisk
 
-gdisk:	$(LIB_OBJS) gdisk.o
-	$(CXX) $(LIB_OBJS) gdisk.o -luuid -o gdisk.exe
+gdisk:	$(LIB_OBJS) gpttext.o gdisk.o
+	$(CXX) $(LIB_OBJS) gpttext.o gdisk.o -luuid -o gdisk.exe
 
 sgdisk: $(LIB_OBJS) sgdisk.o
 	$(CXX) $(LIB_OBJS) sgdisk.o -lpopt -o sgdisk.exe
diff --git a/README b/README
index 9bf133c..2a41fe3 100644
--- a/README
+++ b/README
@@ -69,7 +69,9 @@
 When all the necessary development tools and libraries are installed, you
 can uncompress the package and type "make" at the command prompt in the
 resulting directory. (You may need to type "make -f Makefile.mac" on Mac OS
-X or "make -f Makefile.mingw" to compile using MinGW for Windows.) The
+X or "make -f Makefile.mingw" to compile using MinGW for Windows.) You may
+also need to add header (include) directories or library directories by
+setting the CXXFLAGS environment variable or by editing the Makefile. The
 result should be program files called gdisk and sgdisk. Typing "make gdisk"
 or "make sgdisk" will compile only the requested programs. You can use
 these programs in place or copy the files to a suitable directory, such as
@@ -89,12 +91,12 @@
 RAID arrays over 2TiB in size, though.
 
 My main development platform is a system running the 64-bit version of
-Ubuntu 8.04. I've also tested on 64-bit OpenSuSE, 32-bit Fedora 10, 32-bit
-Fedora 11, 32-bit Ubuntu 6.10, 64-bit Ubunut 9.10, 64-bit Gentoo, 32-bit
-PowerPC Debian Linux, 32-bit Intel-based Mac OS X 10.5 and 10.6, and 64-bit
-FreeBSD 7.1. Problems relating to 64-bit integers on the 32-bit Linux have
-been common during development and may crop up in the future. The Mac OS X,
-FreeBSD, and big-endian (PowerPC) support are new.
+Gentoo Linux (previously Ubuntu 8.04). I've also tested on 64-bit OpenSuSE,
+32-bit Fedora 10, 32-bit Fedora 11, 32-bit Ubuntu 6.10, 64-bit Ubunut 9.10,
+32-bit PowerPC Debian Linux, 32-bit Intel-based Mac OS X 10.5 and 10.6, and
+64-bit FreeBSD 7.1. Problems relating to 64-bit integers on the 32-bit
+Linux have been common during development and may crop up in the future.
+The Mac OS X, FreeBSD, and big-endian (PowerPC) support are new.
 
 Redistribution
 --------------
diff --git a/README.Windows b/README.Windows
index 908c40f..4ff6011 100644
--- a/README.Windows
+++ b/README.Windows
@@ -16,7 +16,8 @@
 the gdisk.html document (the Linux man page converted to HTML format) for

 detailed use information. My GPT fdisk Web page,

 http://www.rodsbooks.com/gdisk/, provides a more tutorial introduction to

-the software.

+the software. I originally wrote GPT fdisk on Linux, and some Linux- and

+Unix-centric language remains in the documentation.

 

 Windows Use Notes

 -----------------

@@ -82,36 +83,32 @@
 Source Code and Compilation Issues

 ----------------------------------

 

-As of version 0.6.2, I haven't been able to get the code to detect the disk

-sector size to work under Windows, so the Windows binary always assumes a

-512-byte sector size. If you use a disk with another sector size, you'll

-have to change this assumption in the source code (in diskio-windows.cc),

-use a version for another platform, or use a different partitioning tool

-altogether.

+I have successfully compiled GPT fdisk using two different Windows

+compilers:

 

-I compiled gdisk.exe using MinGW (http://www.mingw.org), and in particular

-its Linux-hosted cross-compiler. Under Ubuntu Linux, the Makefile.mingw

-file enables compilation of the software via MinGW. (Type "make -f

-Makefile.mingw" to compile the software.) If you try to compile using

-another compiler or even using MinGW under Windows or another Linux

-variety, you may need to adjust the Makefile.mingw options.

+- MinGW (http://www.mingw.org), and in particular its Linux-hosted

+  cross-compiler -- Under Fedora Linux, the Makefile.mingw file enables

+  compilation of the software via MinGW. (Type "make -f Makefile.mingw" to

+  compile the software.) If you try to compile using another compiler or

+  even using MinGW under Windows or another Linux variety, you may need to

+  adjust the Makefile.mingw options.

 

-I've also attempted to compile the code with OpenWatcom 1.8 and Microsoft

-Visual C++ 2008 Express. My OpenWatcom attempts failed, mostly because the

-compiler can't yet handle iostream output on standard C++ strings.

-OpenWatcom also seems to have incorrectly set the value of UINT32_MAX as if

-uint32_t values were 64-bit integers. This alone won't cause the compile to

-fail, but it would create bugs.

+- Microsoft Visual C++ 2008 Express

+  (http://www.microsoft.com/express/Windows/) -- This compiler requires a

+  third-party stdint.h file (I used the one from

+  http://msinttypes.googlecode.com/svn/trunk/stdint.h), but it otherwise

+  works fine. A project is easily created by adding all the *.h files and

+  all the *.cc files except diskio-unix.cc and sgdisk.cc.

 

-My attemps with Visual C++ were much more successful; after tracking down

-and installing a stdint.h file (I used the one from

-http://msinttypes.googlecode.com/svn/trunk/stdint.h) and making a few other

-minor changes, the code compiled fine, and seems to run properly, although

-I've not tested it extensively. I created native projects for both

-OpenWatcom and Visual C++, ignoring the Makefile approach, but I'm not

-including the relevant project files in the source tarball, since they're

-easy enough to regenerate -- just include all the *.h files and all the

-*.cc files except diskio-unix.cc and sgdisk.cc, then build.

+The MinGW compiler produces much larger executables than does the MS

+compiler. The resulting binaries seem to work equally well, but my testing

+has been minimal.

+

+I've also attempted to compile the code with OpenWatcom 1.8, but this

+attempt failed, mostly because the compiler can't yet handle iostream

+output on standard C++ strings. OpenWatcom also seems to have incorrectly

+set the value of UINT32_MAX as if uint32_t values were 64-bit integers.

+This alone won't cause the compile to fail, but it would create bugs.

 

 If you modify GPT fdisk to get it to compile under another compiler, I

 welcome submission of patches.

diff --git a/current.spec b/current.spec
index 707d7b5..4eeedde 100644
--- a/current.spec
+++ b/current.spec
@@ -1,11 +1,11 @@
 Summary: An fdisk-like partitioning tool for GPT disks
 Name: gdisk
-Version: 0.6.4
+Version: 0.6.5
 Release: 1%{?dist}
 License: GPLv2
 URL: http://www.rodsbooks.com/gdisk
 Group: Applications/System
-Source: http://www.rodsbooks.com/gdisk/gdisk-0.6.4.tgz
+Source: http://www.rodsbooks.com/gdisk/gdisk-0.6.5.tgz
 BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
 
 %description
@@ -19,7 +19,7 @@
 %setup -q
 
 %build
-make CFLAGS="$RPM_OPT_FLAGS"
+make CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_CXX_FLAGS"
 
 %install
 rm -rf $RPM_BUILD_ROOT
@@ -40,5 +40,5 @@
 %doc %{_mandir}/man8*
 
 %changelog
-* Fri Feb 19 2010 R Smith <rodsmith@rodsbooks.com> - 0.6.4
-- Created spec file for 0.6.4 release
+* Sun Mar 7 2010 R Smith <rodsmith@rodsbooks.com> - 0.6.5
+- Created spec file for 0.6.5 release
diff --git a/diskio-unix.cc b/diskio-unix.cc
index e955739..7d24301 100644
--- a/diskio-unix.cc
+++ b/diskio-unix.cc
@@ -34,7 +34,9 @@
    realFilename = userFilename;
 } // DiskIO::MakeRealName()
 
-// Open the currently on-record file for reading
+// Open the currently on-record file for reading. Returns 1 if the file is
+// already open or is opened by this call, 0 if opening the file doesn't
+// work.
 int DiskIO::OpenForRead(void) {
    int shouldOpen = 1;
 
@@ -49,10 +51,11 @@
    if (shouldOpen) {
       fd = open(realFilename.c_str(), O_RDONLY);
       if (fd == -1) {
-         cerr << "Problem opening " << realFilename << " for reading! Error is " << errno << "\n";
-         if (errno == EACCES) { // User is probably not running as root
+         cerr << "Problem opening " << realFilename << " for reading! Error is " << errno << ".\n";
+         if (errno == EACCES) // User is probably not running as root
             cerr << "You must run this program as root or use sudo!\n";
-         } // if
+         if (errno == ENOENT)
+            cerr << "The specified file does not exist!\n";
          realFilename = "";
          userFilename = "";
          isOpen = 0;
@@ -105,7 +108,7 @@
 
 // Returns block size of device pointed to by fd file descriptor. If the ioctl
 // returns an error condition, print a warning but return a value of SECTOR_SIZE
-// (512)..
+// (512). If the disk can't be opened at all, return a value of 0.
 int DiskIO::GetBlockSize(void) {
    int err = -1, blockSize = 0;
 
@@ -134,6 +137,7 @@
          if ((errno != ENOTTY) && (errno != EINVAL)) {
             cerr << "\aError " << errno << " when determining sector size! Setting sector size to "
                  << SECTOR_SIZE << "\n";
+            cout << "Disk device is " << realFilename << "\n";
          } // if
       } // if (err == -1)
    } // if (isOpen)
diff --git a/diskio-windows.cc b/diskio-windows.cc
index aaf5c64..6c9aee6 100644
--- a/diskio-windows.cc
+++ b/diskio-windows.cc
@@ -64,9 +64,7 @@
                       NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
       if (fd == INVALID_HANDLE_VALUE) {
          CloseHandle(fd);
-         cerr << "Problem opening ";
-         cerr << realFilename;
-         cerr << " for reading!\n";
+         cerr << "Problem opening " << realFilename << " for reading!\n";
          realFilename = "";
          userFilename = "";
          isOpen = 0;
@@ -123,11 +121,11 @@
 } // DiskIO::Close()
 
 // Returns block size of device pointed to by fd file descriptor. If the ioctl
-// returns an error condition, print a warning but return a value of SECTOR_SIZE
-// (512)..
+// returns an error condition, assume it's a disk file and return a value of
+// SECTOR_SIZE (512). If the disk can't be opened at all, return a value of 0.
 int DiskIO::GetBlockSize(void) {
-   int err;
-   DWORD blockSize, junk1, junk2, junk3;
+   DWORD blockSize = 0, retBytes;
+   DISK_GEOMETRY_EX geom;
 
    // If disk isn't open, try to open it....
    if (!isOpen) {
@@ -135,29 +133,11 @@
    } // if
 
    if (isOpen) {
-/*      BOOL WINAPI GetDiskFreeSpace(
-                                   __in   LPCTSTR lpRootPathName,
-                                   __out  LPDWORD lpSectorsPerCluster,
-                                   __out  LPDWORD lpBytesPerSector,
-                                   __out  LPDWORD lpNumberOfFreeClusters,
-                                   __out  LPDWORD lpTotalNumberOfClusters
-                                  ); */
-//      err = GetDiskFreeSpace(realFilename.c_str(), &junk1, &blockSize, &junk2, &junk3);
-      // Above call is fubared -- returns weird values for blockSize....
-      err = 1;
-      blockSize = 512;
-
-      if (err == 0) {
+	  if (DeviceIoControl(fd, IOCTL_DISK_GET_DRIVE_GEOMETRY_EX, NULL, 0, &geom, sizeof(geom), &retBytes, NULL)) {
+         blockSize = geom.Geometry.BytesPerSector;
+	  } else { // was probably an ordinary file; set default value....
          blockSize = SECTOR_SIZE;
-         // ENOTTY = inappropriate ioctl; probably being called on a disk image
-         // file, so don't display the warning message....
-         // 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 != 267) { // 267 is returned on ordinary files
-            cerr << "\aError " << GetLastError() << " when determining sector size! "
-                 << "Setting sector size to " << SECTOR_SIZE << "\n";
-         } // if
-      } // if (err == -1)
+	  } // if/else
    } // if (isOpen)
 
    return (blockSize);
diff --git a/diskio.cc b/diskio.cc
index bb5bea7..a276b5d 100644
--- a/diskio.cc
+++ b/diskio.cc
@@ -111,7 +111,6 @@
    int err = -2, errnum = 0, result = 8, physicalSectorSize = 4096;
    uint64_t diskSize;
 
-   cout << "Entering FindAlignment()\n";
 #if defined (__linux__) && defined (BLKPBSZGET)
    err = ioctl(fd, BLKPBSZGET, &physicalSectorSize);
    cout << "In FindAlignment(), physicalSectorSize = " << physicalSectorSize
@@ -137,7 +136,7 @@
       // user if it's found....
       diskSize = disksize(fd, &errnum);
       if ((diskSize % (uint64_t) result) != 0) {
-         fprintf(stderr, "\aWarning! Disk size (%llu) is not a multiple of alignment\n"
+         fprintf(stderr, "\aWarning! Disk size (%I64u) is not a multiple of alignment\n"
                          "size (%d), but it should be! Check disk manual and jumper settings!\n",
                          (unsigned long long) diskSize, result);
 } // if
diff --git a/gdisk.8 b/gdisk.8
index c83c0da..037bd0d 100644
--- a/gdisk.8
+++ b/gdisk.8
@@ -1,6 +1,6 @@
-.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" Copyright 2010 Roderick W. Smith (rodsmith@rodsbooks.com)
 .\" May be distributed under the GNU General Public License
-.TH "GDISK" "8" "0.6.4" "Roderick W. Smith" "GPT fdisk Manual"
+.TH "GDISK" "8" "0.6.5" "Roderick W. Smith" "GPT fdisk Manual"
 .SH "NAME"
 gdisk \- Interactive GUID partition table (GPT) manipulator
 .SH "SYNOPSIS"
@@ -9,7 +9,6 @@
 .I device
 
 .SH "DESCRIPTION"
-
 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
@@ -102,9 +101,10 @@
 .B *
 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 (roughly 32 to 200 KiB), but you
-should consult your boot loader documentation for details.
+boot loader is stored, possibly without the benefit of a filesystem. (GRUB2
+may optionally use such a partition.) This partition can typically be quite
+small (roughly 32 to 200 KiB), but you should consult your boot loader
+documentation for details.
 
 .TP 
 .B *
@@ -139,10 +139,10 @@
 .TP 
 .B b
 Save partition data to a backup file. You can back up your current
-in-memory partition table to a disk file using this option. The resulting
+in\-memory partition table to a disk file using this option. The resulting
 file is a binary file consisting of the protective MBR, the main GPT
 header, the backup GPT header, and one copy of the partition table, in that
-order. Note that the backup is of the current in-memory data structures, so
+order. Note that the backup is of the current in\-memory data structures, so
 if you launch the program, make changes, and then use this option, the
 backup will reflect your changes. Note also that the restore option is on
 the recovery & transformation menu; the backup option is on the main menu
@@ -312,15 +312,17 @@
 
 .TP 
 .B g
-Convert GPT into MBR and exit. This option converts up to four GPT partitions
+Convert GPT into MBR and exit. This option converts as many partitions as possible
 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.
+Note that this function generates up to four primary MBR partitions or three
+primary partitions and as many logical partitions as can be generated. Each
+logical partition requires at least one unallocated block immediately before
+its first block. Therefore, it may be possible to convert a maximum of four
+partitions on disks with tightly\-packed partitions; however, if free space was
+inserted between partitions when they were created, and if the disk is under
+2 TiB in size, it should be possible to convert all the partitions to MBR form.
+See also the 'h' option.
 
 .TP 
 .B h
@@ -329,7 +331,9 @@
 the partitions on the disk by creating MBR entries for them. Note that
 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.
+Thus, you may need to recreate the hybrid MBR if you use such tools. Unlike
+the 'g' option, this option does not support converting any partitions into
+MBR logical partitions.
 
 .TP 
 .B i
@@ -416,15 +420,15 @@
 same GUID on two partitions because of buggy GUID assignments (hopefully
 not in \fBgdisk\fR) or sheer incredible coincidence.
 
-.TP
+.TP 
 .B d
 Display the number of logical sectors per physical sector. This value
 determines the sector alignment that GPT fdisk enforces. See the
 description of the 'l' option for more details. Note that this value is
-only auto-detected on Linux with a 2.6.32 kernel or later; on other
+only auto\-detected on Linux with a 2.6.32 kernel or later; on other
 platforms, it defaults to 8.
 
-.TP
+.TP 
 .B e
 Move backup GPT data structures to the end of the disk. Use this command if
 you've added disks to a RAID array, thus creating a virtual disk with space
@@ -442,14 +446,14 @@
 Show detailed partition information. This option is identical to the 'i'
 option on the main menu.
 
-.TP
+.TP 
 .B l
 Change the number of logical sectors per physical sector. Prior to December
-of 2009, most hard disks used 512-byte physical sectors. Starting in
+of 2009, most hard disks used 512\-byte physical sectors. Starting in
 December of 2009, disk manufacturers began transitioning to disks with
-larger physical sectors, but their firmware translated to 512-byte logical
+larger physical sectors, but their firmware translated to 512\-byte logical
 sectors to maintain compatibility with older OSes. If partitions begin
-mid-physical-sector, though, performance can suffer on such drives, since
+mid\-physical\-sector, though, performance can suffer on such drives, since
 important filesystem data structures can span physical sectors on the disk.
 To minimize such problems, GPT fdisk aligns the start of partitions on the
 boundary of presumed physical sectors. You can set the number of logical
@@ -497,7 +501,7 @@
 sizes also work fine. OSes may impose their own limits on the number of
 partitions, though.
 
-.TP
+.TP 
 .B t
 Swap two partitions' entries in the partition table. One partition may be
 empty. For instance, if partitions 1\-4 are defined, transposing 1 and 5
@@ -531,13 +535,13 @@
 usually bypasses the prompt entirely.
 
 .SH "BUGS"
-As of January 2010 (version 0.5.3), \fBgdisk\fR
+As of March 2010 (version 0.6.5), \fBgdisk\fR
 should be considered beta software. Known bugs and limitations include:
 
 .TP 
 .B *
-The program compiles correctly only on Linux, FreeBSD, and Mac OS X. Linux
-versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been
+The program compiles correctly only on Linux, FreeBSD, Mac OS X, and Windows.
+Linux versions for x86\-64 (64\-bit), x86 (32\-bit), and PowerPC (32\-bit) have been
 tested, with the x86\-64 version having seen the most testing.
 
 .TP 
@@ -607,7 +611,7 @@
 Booting after converting an MBR or BSD disklabel disk is likely to be
 disrupted. Sometimes re\-installing a boot loader will fix the problem, but
 other times you may need to switch boot loaders. Except on EFI\-based
-platforms, Windows through at least Windows 7 RC doesn't support booting
+platforms, Windows through at least Windows 7 doesn't support booting
 from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery &
 transformation menu) or abandoning GPT in favor of MBR may be your only
 options in this case.
@@ -633,6 +637,7 @@
 \fBmkfs (8)\fR,
 \fBparted (8)\fR,
 \fBsfdisk (8)\fR
+\fBsgdisk (8)\fR
 
 \fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
 
diff --git a/gdisk.cc b/gdisk.cc
index 7840415..fc0355c 100644
--- a/gdisk.cc
+++ b/gdisk.cc
@@ -7,12 +7,12 @@
 /* This program is copyright (c) 2009, 2010 by Roderick W. Smith. It is distributed
   under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
 
-//#include <iostream>
 #include <stdio.h>
 //#include <getopt.h>
 #include <string.h>
 #include <string>
 #include <iostream>
+#include <sstream>
 #include "mbr.h"
 #include "gpttext.h"
 #include "support.h"
@@ -24,34 +24,44 @@
 void ShowExpertCommands(void);
 void RecoveryMenu(string filename, GPTDataTextUI* theGPT);
 void ShowRecoveryCommands(void);
+void WinWarning(void);
 
 int main(int argc, char* argv[]) {
    GPTDataTextUI theGPT;
-   int doMore = 1;
-   char* device = NULL;
+   int doMore = 1, i;
+   char *device = NULL, *junk;
 
    cout << "GPT fdisk (gdisk) version " << GPTFDISK_VERSION << "\n\n";
 
-    if (argc == 2) { // basic usage
-      if (SizesOK()) {
-#ifdef _WIN32
-         cout << "\a************************************************************************\n"
-               << "Most versions of Windows cannot boot from a GPT disk, and most varieties\n"
-               << "prior to Vista cannot read GPT disks. Therefore, you should exit now\n"
-               << "unless you understand the implications of converting MBR to GPT, editing\n"
-               << "an existing GPT disk, or creating a new GPT disk layout!\n"
-               << "************************************************************************\n\n";
-         cout << "Are you SURE you want to continue? ";
-         if (GetYN() != 'Y')
-            exit(0);
-#endif
+   if (!SizesOK())
+      exit(1);
+
+   switch (argc) {
+      case 1:
+         WinWarning();
+         cout << "Type device filename, or press <Enter> to exit: ";
+         device = new char[255];
+         junk = fgets(device, 255, stdin);
+         if (device[0] != '\n') {
+            i = strlen(device);
+            if (i > 0)
+               if (device[i - 1] == '\n')
+                  device[i - 1] = '\0';
+            doMore = theGPT.LoadPartitions(device);
+            if (doMore) {
+               MainMenu(device, &theGPT);
+            } // if (doMore)
+         } // if
+         delete[] device;
+         break;
+      case 2: // basic usage
+         WinWarning();
          doMore = theGPT.LoadPartitions(argv[1]);
          if (doMore) {
             MainMenu(argv[1], &theGPT);
          } // if (doMore)
-      } // if (SizesOK())
-   } else if (argc == 3) { // usage with "-l" option
-      if (SizesOK()) {
+         break;
+      case 3: // usage with "-l" option
          if (strcmp(argv[1], "-l") == 0) {
             device = argv[2];
          } else if (strcmp(argv[2], "-l") == 0) {
@@ -64,10 +74,11 @@
             doMore = theGPT.LoadPartitions((string) device);
             if (doMore) theGPT.DisplayGPTData();
          } // if
-      } // if
-   } else {
-      cerr << "Usage: " << argv[0] << " [-l] device_file\n";
-   } // if/else
+         break;
+      default:
+         cerr << "Usage: " << argv[0] << " [-l] device_file\n";
+         break;
+   } // switch
 } // main
 
 // Accept a command and execute it. Returns only when the user
@@ -177,7 +188,7 @@
 void RecoveryMenu(string filename, GPTDataTextUI* theGPT) {
    char command, line[255], buFile[255];
    char* junk;
-   uint32_t temp1;
+   uint32_t temp1, numParts;
    int goOn = 1;
 
    do {
@@ -217,16 +228,17 @@
             } // if
             break;
          case 'g': case 'G':
+            numParts = theGPT->GetNumParts();
             temp1 = theGPT->XFormToMBR();
             if (temp1 > 0) {
-               cout << "Converted " << temp1 << " partitions. Finalize and exit? ";
+               cout << "\nConverted " << temp1 << " partitions. Finalize and exit? ";
                if (GetYN() == 'Y') {
-                  if (theGPT->DestroyGPT() > 0)
+                  if ((theGPT->DestroyGPT() > 0) && (theGPT->SaveMBR()))
                      goOn = 0;
                } else {
                   theGPT->MakeProtectiveMBR();
-                  theGPT->WriteProtectiveMBR();
-                  cout << "Note: New protective MBR created.\n";
+                  theGPT->SetGPTSize(numParts);
+                  cout << "Note: New protective MBR created\n\n";
                } // if/else
             } // if
             break;
@@ -414,3 +426,19 @@
    cout << "z\tzap (destroy) GPT data structures and exit\n";
    cout << "?\tprint this menu\n";
 } // ShowExpertCommands()
+
+// On Windows, display a warning and ask whether to continue. If the user elects
+// not to continue, exit immediately.
+void WinWarning(void) {
+#ifdef _WIN32
+   cout << "\a************************************************************************\n"
+   << "Most versions of Windows cannot boot from a GPT disk, and most varieties\n"
+   << "prior to Vista cannot read GPT disks. Therefore, you should exit now\n"
+   << "unless you understand the implications of converting MBR to GPT, editing\n"
+   << "an existing GPT disk, or creating a new GPT disk layout!\n"
+   << "************************************************************************\n\n";
+   cout << "Are you SURE you want to continue? ";
+   if (GetYN() != 'Y')
+      exit(0);
+#endif
+} // WinWarning()
diff --git a/gpt.cc b/gpt.cc
index e213205..ac42995 100644
--- a/gpt.cc
+++ b/gpt.cc
@@ -25,6 +25,7 @@
 #include "parttypes.h"
 #include "attributes.h"
 #include "diskio.h"
+#include "partnotes.h"
 
 using namespace std;
 
@@ -209,6 +210,9 @@
    // Check for overlapping partitions....
    problems += FindOverlaps();
 
+   // Check for insane partitions (start after end, hugely big, etc.)
+   problems += FindInsanePartitions();
+
    // Check for mismatched MBR and GPT partitions...
    problems += FindHybridMismatches();
 
@@ -278,7 +282,7 @@
       } // Problem at start of disk
       if (mainHeader.lastUsableLBA < lastUsedBlock) {
          overlap = lastUsedBlock - mainHeader.lastUsableLBA;
-         cout << "Warning! Secondary partition table overlaps the last partition by "
+         cout << "\nWarning! Secondary partition table overlaps the last partition by\n"
               << overlap << " blocks!\n";
          if (lastUsedBlock > (diskSize - 2)) {
             cout << "You will need to delete this partition or resize it in another utility.\n";
@@ -517,6 +521,28 @@
    return problems;
 } // GPTData::FindOverlaps()
 
+// Find partitions that are insane -- they start after they end or are too
+// big for the disk. (The latter should duplicate detection of overlaps
+// with GPT backup data structures, but better to err on the side of
+// redundant tests than to miss something....)
+int GPTData::FindInsanePartitions(void) {
+   uint32_t i;
+   int problems = 0;
+
+   for (i = 0; i < mainHeader.numParts; i++) {
+      if (partitions[i].GetFirstLBA() > partitions[i].GetLastLBA()) {
+         problems++;
+         cout << "\nProblem: partition " << i << " ends before it begins.\n";
+      } // if
+      if (partitions[i].GetLastLBA() >= diskSize) {
+         problems++;
+      cout << "\nProblem: partition " << i << " is too big for the disk.\n";
+      } // if
+   } // for
+   return problems;
+} // GPTData::FindInsanePartitions(void)
+
+
 /******************************************************************
  *                                                                *
  * Begin functions that load data from disk or save data to disk. *
@@ -560,25 +586,24 @@
 int GPTData::LoadPartitions(const string & deviceFilename) {
    BSDData bsdDisklabel;
    int err, allOK = 1;
-   uint32_t i;
-   uint64_t firstBlock, lastBlock;
    MBRValidity mbrState;
 
-   // First, do a test to see if writing will be possible later....
-   err = myDisk.OpenForWrite(deviceFilename);
-   if ((err == 0) && (!justLooking)) {
-      cout << "\aNOTE: Write test failed with error number " << errno
-           << ". It will be impossible to save\nchanges to this disk's partition table!\n";
-#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
-      cout << "You may be able to enable writes by exiting this program, typing\n"
-           << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
-           << "program.\n";
-#endif
-      cout << "\n";
-   } // if
-   myDisk.Close();
-
    if (myDisk.OpenForRead(deviceFilename)) {
+      err = myDisk.OpenForWrite(deviceFilename);
+      if ((err == 0) && (!justLooking)) {
+         cout << "\aNOTE: Write test failed with error number " << errno
+              << ". It will be impossible to save\nchanges to this disk's partition table!\n";
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+         cout << "You may be able to enable writes by exiting this program, typing\n"
+              << "'sysctl kern.geom.debugflags=16' at a shell prompt, and re-running this\n"
+              << "program.\n";
+#endif
+         cout << "\n";
+      } // if
+      myDisk.Close(); // Close and re-open read-only in case of bugs
+   } else allOK = 0; // if
+
+   if (allOK && myDisk.OpenForRead(deviceFilename)) {
       // store disk information....
       diskSize = myDisk.DiskSize(&err);
       blockSize = (uint32_t) myDisk.GetBlockSize();
@@ -613,26 +638,11 @@
             break;
       } // switch
 
-      // Now find the first and last sectors used by partitions...
-      if (allOK) {
-         firstBlock = mainHeader.backupLBA; // start high
-         lastBlock = 0; // start low
-         for (i = 0; i < mainHeader.numParts; i++) {
-            if ((partitions[i].GetFirstLBA() < firstBlock) &&
-                 (partitions[i].GetFirstLBA() > 0))
-               firstBlock = partitions[i].GetFirstLBA();
-            if (partitions[i].GetLastLBA() > lastBlock)
-               lastBlock = partitions[i].GetLastLBA();
-         } // for
+      if (allOK)
          CheckGPTSize();
-      } // if
+      myDisk.Close();
    } else {
       allOK = 0;
-      cerr << "Problem opening " << deviceFilename << " for reading! Error is "
-           << errno << "\n";
-      if (errno == EACCES) { // User is probably not running as root
-         cerr << "You must run this program as root or use sudo!\n";
-      } // if
    } // if/else
    return (allOK);
 } // GPTData::LoadPartitions()
@@ -768,11 +778,12 @@
 
    // Reverse byte order, if necessary
    if (IsLittleEndian() == 0) {
-      ReverseHeaderBytes(header);
+      ReverseHeaderBytes(&tempHeader);
    } // if
 
-   if (allOK && mainHeader.numParts != tempHeader.numParts)
+   if (allOK && (mainHeader.numParts != tempHeader.numParts) && *crcOk) {
       allOK = SetGPTSize(tempHeader.numParts);
+   }
 
    *header = tempHeader;
    return allOK;
@@ -793,8 +804,9 @@
       } else {
          retval = disk.Seek(sector);
       } // if/else
+      if (retval == 1)
+         retval = SetGPTSize(header.numParts);
       if (retval == 1) {
-         SetGPTSize(header.numParts);
          sizeOfParts = header.numParts * header.sizeOfPartitionEntries;
          if (disk.Read(partitions, sizeOfParts) != (int) sizeOfParts) {
             cerr << "Warning! Read error " << errno << "! Misbehavior now likely!\n";
@@ -848,7 +860,6 @@
 int GPTData::SaveGPTData(int quiet) {
    int allOK = 1, littleEndian;
    char answer;
-   uint32_t numParts;
 
    littleEndian = IsLittleEndian();
 
@@ -890,8 +901,8 @@
       } // if correction requested
    } // if
 
-   // Check for overlapping partitions....
-   if (FindOverlaps() > 0) {
+   // Check for overlapping or insane partitions....
+   if ((FindOverlaps() > 0) || (FindInsanePartitions() > 0)) {
       allOK = 0;
       cerr << "Aborting write operation!\n";
    } // if
@@ -900,10 +911,6 @@
    // (function displays warning message)
    FindHybridMismatches();
 
-   // Pull out some data that's needed before doing byte-order reversal on
-   // big-endian systems....
-   numParts = mainHeader.numParts;
-
    RecomputeCRCs();
 
    if ((allOK) && (!quiet)) {
@@ -1127,7 +1134,7 @@
 } // GPTData::LoadGPTBackup()
 
 int GPTData::SaveMBR(void) {
-   return protectiveMBR.WriteMBRData();
+   return protectiveMBR.WriteMBRData(&myDisk);
 } // GPTData::SaveMBR()
 
 // This function destroys the on-disk GPT structures, but NOT the on-disk
@@ -1413,7 +1420,7 @@
       partitions[partNum].BlankPartition();
    } // if
    return numDone;
-} // GPTData::XFormDisklable(int i)
+} // GPTData::XFormDisklabel(uint32_t i)
 
 // Transform the partitions on an already-loaded BSD disklabel...
 int GPTData::XFormDisklabel(BSDData* disklabel) {
@@ -1477,44 +1484,45 @@
    return allOK;
 } // GPTData::OnePartToMBR()
 
-// Convert up to four partitions to MBR form and return the number done.
-// Partitions are specified in an array of GPT partition numbers,
-// with an associated array of partition type codes. Both must be
-// at least four elements in size (longer is OK, but will be ignored).
-// A partition number of MBR_EFI_GPT means to place an EFI GPT
-// protective partition in that location in the table (the associated
-// mbrType[] should be 0xEE), and MBR_EMPTY means not to create a
-// partition in that table position. If the mbrType[] entry for a
-// partition is 0, a default entry is used, based on the GPT
-// partition type code.
+// Convert partitions to MBR form (primary and logical) and return
+// the number done. Partitions are specified in a PartNotes variable,
+// which includes pointers to GPT partition numbers. A partition number
+// of MBR_EFI_GPT means to place an EFI GPT protective partition in that
+// location in the table, and MBR_EMPTY means not to create a partition
+// in that table position. If the partition type entry for a partition
+// is 0, a default entry is used, based on the GPT partition type code.
 // Returns the number of partitions converted, NOT counting EFI GPT
-// protective partitions.
-int GPTData::PartsToMBR(const int *gptParts, const int *mbrTypes) {
-   int i, numConverted = 0;
+// protective partitions or extended partitions.
+int GPTData::PartsToMBR(PartNotes & notes) {
+   int mbrNum = 0, numConverted = 0;
+   struct PartInfo convInfo;
 
-   if ((gptParts != NULL) && (mbrTypes != NULL)) {
-      protectiveMBR.EmptyMBR();
-      protectiveMBR.SetDiskSize(diskSize);
-      // Do two passes, one to get "real" partitions and
-      // the next to create EFI GPT protective partition(s)
-      for (i = 0; i < 4; i++) {
-         if (gptParts[i] >= 0) {
-            numConverted += OnePartToMBR((uint32_t) gptParts[i], i);
-            if (mbrTypes[i] != 0)
-               protectiveMBR.SetPartType(i, mbrTypes[i]);
-         } // if
-      } // for (regular partition pass)
-      for (i = 0; i < 4; i++) {
-         if (gptParts[i] == MBR_EFI_GPT) {
-            if (protectiveMBR.FindFirstAvailable() == UINT32_C(1)) {
-               protectiveMBR.MakePart(i, 1, protectiveMBR.FindLastInFree(1), mbrTypes[i]);
-               protectiveMBR.SetHybrid();
-            } else {
-               protectiveMBR.MakeBiggestPart(i, mbrTypes[i]);
-            } // if/else
-         } // if EFI GPT partition specified
-      } // for (0xEE pass)
-   } // if arrays were passed
+   protectiveMBR.EmptyMBR();
+   protectiveMBR.SetDiskSize(diskSize);
+   notes.Rewind();
+   while (notes.GetNextInfo(&convInfo) >= 0) {
+      if ((convInfo.gptPartNum >= 0) && (convInfo.type == PRIMARY)) {
+         numConverted += OnePartToMBR((uint32_t) convInfo.gptPartNum, mbrNum);
+         if (convInfo.hexCode != 0)
+            protectiveMBR.SetPartType(mbrNum, convInfo.hexCode);
+         if (convInfo.active)
+            protectiveMBR.SetPartBootable(mbrNum);
+         mbrNum++;
+      } // if
+      if (convInfo.gptPartNum == MBR_EFI_GPT) {
+         if (protectiveMBR.FindFirstAvailable() == UINT32_C(1)) {
+            protectiveMBR.MakePart(mbrNum, 1, protectiveMBR.FindLastInFree(1), convInfo.hexCode);
+            protectiveMBR.SetHybrid();
+         } else {
+            protectiveMBR.MakeBiggestPart(mbrNum, convInfo.hexCode);
+         } // if/else
+         mbrNum++;
+      } // if EFI GPT partition specified
+   } // for
+   // Now do logical partition(s)...
+   protectiveMBR.SetDisk(&myDisk);
+   numConverted += protectiveMBR.CreateLogicals(notes);
+//   numConverted += PartsToLogical(notes);
    return numConverted;
 } // GPTData::PartsToMBR()
 
@@ -1545,12 +1553,12 @@
    } // if
 
    // Do the work only if the # of partitions is changing. Along with being
-   // efficient, this prevents mucking the with location of the secondary
+   // efficient, this prevents mucking with the location of the secondary
    // partition table, which causes problems when loading data from a RAID
    // array that's been expanded because this function is called when loading
    // data.
-   if ((numEntries != mainHeader.numParts) || (numEntries != secondHeader.numParts)
-       || (partitions == NULL)) {
+   if (((numEntries != mainHeader.numParts) || (numEntries != secondHeader.numParts)
+       || (partitions == NULL)) && (numEntries > 0)) {
       newParts = new GPTPart [numEntries * sizeof (GPTPart)];
       if (newParts != NULL) {
          if (partitions != NULL) { // existing partitions; copy them over
@@ -1973,10 +1981,10 @@
       firstMoved = 0;
       for (i = 0; i < mainHeader.numParts; i++) {
          if ((first >= partitions[i].GetFirstLBA()) &&
-              (first <= partitions[i].GetLastLBA())) { // in existing part.
+             (first <= partitions[i].GetLastLBA())) { // in existing part.
             first = partitions[i].GetLastLBA() + 1;
             firstMoved = 1;
-              } // if
+         } // if
       } // for
    } while (firstMoved == 1);
    if (first > mainHeader.lastUsableLBA)
@@ -2023,10 +2031,10 @@
       lastMoved = 0;
       for (i = 0; i < mainHeader.numParts; i++) {
          if ((last >= partitions[i].GetFirstLBA()) &&
-              (last <= partitions[i].GetLastLBA())) { // in existing part.
+             (last <= partitions[i].GetLastLBA())) { // in existing part.
             last = partitions[i].GetFirstLBA() - 1;
             lastMoved = 1;
-              } // if
+         } // if
       } // for
    } while (lastMoved == 1);
    if (last < mainHeader.firstUsableLBA)
@@ -2042,9 +2050,9 @@
    nearestStart = mainHeader.lastUsableLBA;
    for (i = 0; i < mainHeader.numParts; i++) {
       if ((nearestStart > partitions[i].GetFirstLBA()) &&
-           (partitions[i].GetFirstLBA() > start)) {
+          (partitions[i].GetFirstLBA() > start)) {
          nearestStart = partitions[i].GetFirstLBA() - 1;
-           } // if
+      } // if
    } // for
    return (nearestStart);
 } // GPTData::FindLastInFree()
@@ -2077,8 +2085,11 @@
    return totalFound;
 } // GPTData::FindFreeBlocks()
 
-// Returns 1 if sector is unallocated, 0 if it's allocated to a partition
-int GPTData::IsFree(uint64_t sector) {
+// Returns 1 if sector is unallocated, 0 if it's allocated to a partition.
+// If it's allocated, return the partition number to which it's allocated
+// in partNum, if that variable is non-NULL. (A value of UINT32_MAX is
+// returned in partNum if the sector is in use by basic GPT data structures.)
+int GPTData::IsFree(uint64_t sector, uint32_t *partNum) {
    int isFree = 1;
    uint32_t i;
 
@@ -2086,11 +2097,15 @@
       if ((sector >= partitions[i].GetFirstLBA()) &&
            (sector <= partitions[i].GetLastLBA())) {
          isFree = 0;
+         if (partNum != NULL)
+            *partNum = i;
       } // if
    } // for
    if ((sector < mainHeader.firstUsableLBA) ||
         (sector > mainHeader.lastUsableLBA)) {
       isFree = 0;
+      if (partNum != NULL)
+         *partNum = UINT32_MAX;
    } // if
    return (isFree);
 } // GPTData::IsFree()
diff --git a/gpt.h b/gpt.h
index 876ecde..b2657e7 100644
--- a/gpt.h
+++ b/gpt.h
@@ -11,11 +11,12 @@
 #include "mbr.h"
 #include "bsd.h"
 #include "gptpart.h"
+#include "partnotes.h"
 
 #ifndef __GPTSTRUCTS
 #define __GPTSTRUCTS
 
-#define GPTFDISK_VERSION "0.6.5-pre1"
+#define GPTFDISK_VERSION "0.6.5"
 
 // Constants used by GPTData::PartsToMBR(). MBR_EMPTY must be the lowest-
 // numbered value to refer to partition numbers. (Most will be 0 or positive,
@@ -25,6 +26,8 @@
 
 using namespace std;
 
+class PartNotes;
+
 /****************************************
  *                                      *
  * GPTData class and related structures *
@@ -101,6 +104,7 @@
    void RebuildSecondHeader(void);
    int FindHybridMismatches(void);
    int FindOverlaps(void);
+   int FindInsanePartitions(void);
 
    // Load or save data from/to disk
    int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);}
@@ -130,7 +134,7 @@
    virtual int XFormDisklabel(uint32_t partNum);
    int XFormDisklabel(BSDData* disklabel);
    int OnePartToMBR(uint32_t gptPart, int mbrPart); // add one partition to MBR. Returns 1 if successful
-   int PartsToMBR(const int *gptParts, const int *mbrTypes);
+   int PartsToMBR(PartNotes & notes);
 
    // Adjust GPT structures WITHOUT user interaction...
    int SetGPTSize(uint32_t numEntries);
@@ -157,6 +161,10 @@
    uint64_t GetSecondHeaderLBA(void) {return secondHeader.currentLBA;}
    uint64_t GetMainPartsLBA(void) {return mainHeader.partitionEntriesLBA;}
    uint64_t GetSecondPartsLBA(void) {return secondHeader.partitionEntriesLBA;}
+   uint64_t GetFirstUsableLBA(void) {return mainHeader.firstUsableLBA;}
+   uint64_t GetLastUsableLBA(void) {return mainHeader.lastUsableLBA;}
+   uint64_t GetPartFirstLBA(uint32_t i) {return partitions[i].GetFirstLBA();}
+   uint64_t GetPartLastLBA(uint32_t i) {return partitions[i].GetLastLBA();}
    uint32_t CountParts(void);
 
    // Find information about free space
@@ -165,7 +173,7 @@
    uint64_t FindLastAvailable();
    uint64_t FindLastInFree(uint64_t start);
    uint64_t FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment);
-   int IsFree(uint64_t sector);
+   int IsFree(uint64_t sector, uint32_t *partNum = NULL);
    int IsFreePartNum(uint32_t partNum);
 
    // Change how functions work, or return information on same
diff --git a/gptpart.cc b/gptpart.cc
index d82c0f6..a395646 100644
--- a/gptpart.cc
+++ b/gptpart.cc
@@ -26,6 +26,12 @@
 GPTPart::GPTPart(void) {
    int i;
 
+   partitionType.Zero();
+   uniqueGUID.Zero();
+   firstLBA = 0;
+   lastLBA = 0;
+   attributes = 0;
+
    for (i = 0; i < NAME_SIZE; i++)
       name[i] = '\0';
 } // Default constructor
@@ -48,6 +54,7 @@
 // set before the beginning).
 uint64_t GPTPart::GetLengthLBA(void) {
    uint64_t length = 0;
+
    if (firstLBA <= lastLBA)
       length = lastLBA - firstLBA + UINT64_C(1);
    return length;
@@ -86,7 +93,7 @@
 // string, although the GUID partition definition requires a UTF-16LE
 // string. This function creates a simple-minded copy for this.
 void GPTPart::SetName(const string & theName) {
-   char newName[NAME_SIZE]; // New name
+   char newName[NAME_SIZE];
    char *junk;
    int i;
 
@@ -222,8 +229,6 @@
 
 // Reverse the bytes of integral data types; used on big-endian systems.
 void GPTPart::ReversePartBytes(void) {
-//   partitionType.ReverseGUIDBytes();
-//   uniqueGUID.ReverseGUIDBytes();
    ReverseBytes(&firstLBA, 8);
    ReverseBytes(&lastLBA, 8);
    ReverseBytes(&attributes, 8);
@@ -242,6 +247,7 @@
 
    if (GetDescription() == GetTypeName())
       changeName = 1;
+
    cout << "Current type is '" << GetTypeName() << "'\n";
    while ((!partitionType.Valid(typeNum)) && (typeNum != 0)) {
       cout << "Hex code (L to show codes, 0 to enter raw code, Enter = 0700): ";
diff --git a/gpttext.cc b/gpttext.cc
index 3efbdf2..46e6eea 100644
--- a/gpttext.cc
+++ b/gpttext.cc
@@ -22,11 +22,14 @@
 
 #include <string.h>
 #include <errno.h>
+#include <stdint.h>
 #include <iostream>
 #include <sstream>
 #include <cstdio>
 #include "attributes.h"
 #include "gpttext.h"
+#include "partnotes.h"
+#include "support.h"
 
 using namespace std;
 
@@ -130,6 +133,7 @@
    return numDone;
 } // GPTData::XFormDisklabel(int i)
 
+
 /*********************************************************************
  *                                                                   *
  * Begin functions that obtain information from the users, and often *
@@ -186,41 +190,41 @@
    } // while
 
    if (((firstBlock = FindFirstAvailable()) != 0) &&
-      (firstFreePart < mainHeader.numParts)) {
+       (firstFreePart < mainHeader.numParts)) {
       lastBlock = FindLastAvailable();
-   firstInLargest = FindFirstInLargest();
-   
-   // Get partition number....
-   do {
-      prompt1 << "Partition number (" << firstFreePart + 1 << "-" << mainHeader.numParts
-      << ", default " << firstFreePart + 1 << "): ";
-      partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
-                          firstFreePart + 1, prompt1.str()) - 1;
-                          if (partitions[partNum].GetFirstLBA() != 0)
-                             cout << "partition " << partNum + 1 << " is in use.\n";
-   } while (partitions[partNum].GetFirstLBA() != 0);
-   
-   // Get first block for new partition...
-   prompt2 << "First sector (" << firstBlock << "-" << lastBlock << ", default = "
-   << firstInLargest << ") or {+-}size{KMGT}: ";
-   do {
-      sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt2.str());
-   } while (IsFree(sector) == 0);
-   Align(&sector); // Align sector to correct multiple
-   firstBlock = sector;
-   
-   // Get last block for new partitions...
-   lastBlock = FindLastInFree(firstBlock);
-   prompt3 << "Last sector (" << firstBlock << "-" << lastBlock << ", default = "
-   << lastBlock << ") or {+-}size{KMGT}: ";
-   do {
-      sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt3.str());
-   } while (IsFree(sector) == 0);
-   lastBlock = sector;
-   
-   firstFreePart = GPTData::CreatePartition(partNum, firstBlock, lastBlock);
-   partitions[partNum].ChangeType();
-   partitions[partNum].SetDefaultDescription();
+      firstInLargest = FindFirstInLargest();
+
+      // Get partition number....
+      do {
+         prompt1 << "Partition number (" << firstFreePart + 1 << "-" << mainHeader.numParts
+                 << ", default " << firstFreePart + 1 << "): ";
+         partNum = GetNumber(firstFreePart + 1, mainHeader.numParts,
+                             firstFreePart + 1, prompt1.str()) - 1;
+         if (partitions[partNum].GetFirstLBA() != 0)
+            cout << "partition " << partNum + 1 << " is in use.\n";
+      } while (partitions[partNum].GetFirstLBA() != 0);
+
+      // Get first block for new partition...
+      prompt2 << "First sector (" << firstBlock << "-" << lastBlock << ", default = "
+              << firstInLargest << ") or {+-}size{KMGT}: ";
+      do {
+         sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, prompt2.str());
+      } while (IsFree(sector) == 0);
+      Align(&sector); // Align sector to correct multiple
+      firstBlock = sector;
+
+      // Get last block for new partitions...
+      lastBlock = FindLastInFree(firstBlock);
+      prompt3 << "Last sector (" << firstBlock << "-" << lastBlock << ", default = "
+            << lastBlock << ") or {+-}size{KMGT}: ";
+      do {
+         sector = GetSectorNum(firstBlock, lastBlock, lastBlock, prompt3.str());
+      } while (IsFree(sector) == 0);
+      lastBlock = sector;
+
+      firstFreePart = GPTData::CreatePartition(partNum, firstBlock, lastBlock);
+      partitions[partNum].ChangeType();
+      partitions[partNum].SetDefaultDescription();
    } else {
       cout << "No free sectors available\n";
    } // if/else
@@ -246,7 +250,7 @@
 void GPTDataTextUI::ChangePartType(void) {
    int partNum;
    uint32_t low, high;
-   
+
    if (GetPartRange(&low, &high) > 0) {
       partNum = GetPartNum();
       partitions[partNum].ChangeType();
@@ -259,7 +263,7 @@
 // adjust them for completeness....
 void GPTDataTextUI::SetAttributes(uint32_t partNum) {
    Attributes theAttr;
-   
+
    theAttr.SetAttributes(partitions[partNum].GetAttributes());
    theAttr.DisplayAttributes();
    theAttr.ChangeAttributes();
@@ -282,7 +286,7 @@
       if (high >= mainHeader.numParts - 1)
          high = 0;
       prompt << "New partition number (1-" << mainHeader.numParts
-      << ", default " << high + 2 << "): ";
+             << ", default " << high + 2 << "): ";
       partNum2 = GetNumber(1, mainHeader.numParts, high + 2, prompt.str()) - 1;
       didIt = GPTData::SwapPartitions(partNum1, partNum2);
    } else {
@@ -325,7 +329,7 @@
 void GPTDataTextUI::ShowDetails(void) {
    int partNum;
    uint32_t low, high;
-   
+
    if (GetPartRange(&low, &high) > 0) {
       partNum = GetPartNum();
       ShowPartDetails(partNum);
@@ -340,9 +344,10 @@
    uint32_t partNums[3];
    char line[255];
    char* junk;
-   int numParts, i, j, mbrNum, bootable = -1;
-   int gptParts[4], mbrTypes[4];
-   char fillItUp = 'M'; // fill extra partition entries? (Yes/No/Maybe)
+   int numParts, i, j, mbrNum, bootable = 0;
+   unsigned int hexCode = 0;
+   struct PartInfo *newNote;
+   PartNotes notes;
    char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
 
    cout << "\nWARNING! Hybrid MBRs are flaky and potentially dangerous! If you decide not\n"
@@ -361,120 +366,189 @@
       eeFirst = GetYN();
    } // if
 
-   for (i = 0; i < 4; i++)
-      gptParts[i] = MBR_EMPTY;
-
    for (i = 0; i < numParts; i++) {
-      j = partNums[i] - 1;
+      newNote = new struct PartInfo;
+      j = newNote->gptPartNum = partNums[i] - 1;
       mbrNum = i + (eeFirst == 'Y');
       cout << "\nCreating entry for GPT partition #" << j + 1
            << " (MBR partition #" << mbrNum + 1 << ")\n";
-      gptParts[mbrNum] = j;
-      mbrTypes[mbrNum] = GetMBRTypeCode(partitions[j].GetHexType() / 256);
+      newNote->hexCode = GetMBRTypeCode(partitions[j].GetHexType() / 256);
+      newNote->firstLBA = partitions[j].GetFirstLBA();
+      newNote->lastLBA = partitions[j].GetLastLBA();
+      newNote->type = PRIMARY;
       cout << "Set the bootable flag? ";
       if (GetYN() == 'Y')
-         bootable = mbrNum;
+         newNote->active = 1;
+      else
+         newNote->active = 0;
+      notes.AddToEnd(newNote);
    } // for
 
    if (numParts > 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.
-      if (eeFirst == 'Y')
-         mbrNum = 0;
-      else
-         mbrNum = numParts;
-      gptParts[mbrNum] = MBR_EFI_GPT;
-      mbrTypes[mbrNum] = 0xEE;
+      newNote = new struct PartInfo;
+      newNote->gptPartNum = MBR_EFI_GPT;
+      newNote->active = 0;
+      newNote->hexCode = 0xEE;
+      newNote->type = PRIMARY;
+      // newNote firstLBA and lastLBA are computed later...
+      if (eeFirst == 'Y') {
+	     notes.AddToStart(newNote);
+      } else {
+	     notes.AddToEnd(newNote);
+      } // else
       protectiveMBR.SetHybrid();
 
       // ... and for good measure, if there are any partition spaces left,
       // optionally create another protective EFI partition to cover as much
       // space as possible....
-      for (i = 0; i < 4; i++) {
-         if (gptParts[i] == MBR_EMPTY) { // unused entry....
-            if (fillItUp == 'M') {
-               cout << "\nUnused partition space(s) found. Use one to protect more partitions? ";
-               fillItUp = GetYN();
-               mbrTypes[i] = 0x00; // use this to flag a need to get type code
-            } // if
-            if (fillItUp == 'Y') {
-               while ((mbrTypes[i] <= 0) || (mbrTypes[i] > 255)) {
-                  cout << "Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ";
-                  // Comment on above: Mac OS treats disks with more than one
-                  // 0xEE MBR partition as MBR disks, not as GPT disks.
-                  junk = fgets(line, 255, stdin);
-                  sscanf(line, "%x", &mbrTypes[i]);
-                  if (line[0] == '\n')
-                     mbrTypes[i] = 0;
-               } // while
-               gptParts[i] = MBR_EFI_GPT;
-            } // if (fillItUp == 'Y')
-         } // if unused entry
-      } // for (i = 0; i < 4; i++)
-      PartsToMBR(gptParts, mbrTypes);
+      if (notes.GetNumPrimary() < 4) { // unused entry....
+         cout << "\nUnused partition space(s) found. Use one to protect more partitions? ";
+         if (GetYN() == 'Y') {
+            while ((hexCode <= 0) || (hexCode > 255)) {
+               cout << "Enter an MBR hex code (EE is EFI GPT, but may confuse MacOS): ";
+               // Comment on above: Mac OS treats disks with more than one
+               // 0xEE MBR partition as MBR disks, not as GPT disks.
+               junk = fgets(line, 255, stdin);
+               sscanf(line, "%x", &hexCode);
+               if (line[0] == '\n')
+                  hexCode = 0x00;
+            } // while
+            newNote = new struct PartInfo;
+            newNote->gptPartNum = MBR_EFI_GPT;
+            newNote->active = 0;
+            newNote->hexCode = hexCode;
+            newNote->type = PRIMARY;
+            // newNote firstLBA and lastLBA are computed later...
+            notes.AddToEnd(newNote);
+         } // if (GetYN() == 'Y')
+      } // if unused entry
+      PartsToMBR(notes);
       if (bootable > 0)
          protectiveMBR.SetPartBootable(bootable);
    } // if (numParts > 0)
 } // GPTDataTextUI::MakeHybrid()
 
-// 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
+// Assign GPT partitions to primary or logical status for conversion. The
+// function first presents a suggested layout with as many logicals as
+// possible, but gives the user the option to override this suggestion.
+// Returns the number of partitions assigned (0 if problems or if the
+// user aborts)
+int GPTDataTextUI::AssignPrimaryOrLogical(PartNotes & notes) {
+   int i, partNum, allOK = 1, changesWanted = 1, countedParts, numPrimary = 0, numLogical = 0;
+   int newNumParts; // size of GPT table
+
+   // Sort and resize the GPT table. Resized to clear the sector before the
+   // first available sector, if possible, so as to enable turning the
+   // first partition into a logical, should that be desirable/necessary.
+   SortGPT();
+   countedParts = newNumParts = CountParts();
+   i = blockSize / GPT_SIZE;
+   if ((newNumParts % i) != 0) {
+      newNumParts = ((newNumParts / i) + 1) * i;
+   } // if
+   SetGPTSize(newNumParts);
+
+   // Takes notes on existing partitions: Create an initial assignment as
+   // primary or logical, set default MBR types, and then make it legal
+   // (drop partitions as required to fit in the MBR and as logicals).
+   allOK = (notes.PassPartitions(partitions, this, mainHeader.numParts, blockSize) ==
+            (int) mainHeader.numParts);
+   for (i = 0; i < countedParts; i++)
+      notes.SetMbrHexType(i, partitions[i].GetHexType() / 255);
+   notes.MakeItLegal();
+
+   while (allOK && changesWanted) {
+      notes.ShowSummary();
+      cout << "\n";
+      partNum = GetNumber(-1, countedParts, -2,
+                          "Type partition to change, 0 to accept, -1 to abort: ");
+      switch (partNum) {
+         case -1:
+            allOK = 0;
+            break;
+         case 0:
+            changesWanted = 0;
+            break;
+         default:
+            allOK = notes.MakeChange(partNum - 1);
+            break;
+      } // switch
+   } // while
+
+   i = 0;
+   if (allOK)
+      for (i = 0; i < countedParts; i++) {
+         switch (notes.GetType(i)) {
+            case PRIMARY:
+               numPrimary++;
+               break;
+            case LOGICAL:
+               numLogical++;
+               break;
+         } // switch
+         if (notes.GetActiveStatus(i))
+            protectiveMBR.SetPartBootable(i);
+      } // for
+
+   if (numPrimary > 4) {
+      cerr << "Warning! More than four primary partitions in "
+           << "GPTDataTextUI::AssignPrimaryOrLogical()!\n";
+      allOK = 0;
+   } // if
+   return (allOK * (numPrimary + numLogical));
+} // GPTDataTextUI::AssignPrimaryOrLogical()
+
+// Convert the GPT to MBR form, storing partitions in the protectiveMBR
+// variable. This function is necessarily limited; it may not be able to
+// convert all partitions, depending on the disk size and available space
+// before each partition (one free sector is required to create a logical
+// partition, which are necessary to convert more than four partitions).
+// 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.
+// the GPT data, call SaveMBR() to save the MBR, and then exit.
 int GPTDataTextUI::XFormToMBR(void) {
-   char line[255];
-   char* junk;
-   int j, numParts, bootable = -1, numConverted = 0, numReallyConverted;
-   int gptParts[4], mbrTypes[4];
-   uint32_t i, partNums[4];
+   int numParts, numToConvert, numReallyConverted = 0;
+   int origNumParts;
+   PartNotes notes;
+   GPTPart *tempGptParts;
+   uint32_t i;
 
-   // Get the numbers of up to four partitions to add to the
-   // hybrid MBR....
+   // Back up partition array, since we'll be sorting it and we want to
+   // be able to restore it in case the user aborts....
+   origNumParts = mainHeader.numParts;
+   tempGptParts = new GPTPart[mainHeader.numParts];
+   for (i = 0; i < mainHeader.numParts; i++)
+      tempGptParts[i] = partitions[i];
+
    numParts = CountParts();
-   cout << "Counted " << numParts << " partitions.\n";
 
-   if (numParts > 4) { // Over four partitions; engage in triage
-      cout << "Type from one to four GPT partition numbers, separated by spaces, to be\n"
-      << "used in the MBR, in sequence: ";
-      junk = 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
+   numToConvert = AssignPrimaryOrLogical(notes);
 
-   for (i = 0; i < 4; i++)
-      gptParts[i] = MBR_EMPTY;
+   if (numToConvert > 0) {
+      numReallyConverted = PartsToMBR(notes);
+      if (numReallyConverted != numToConvert) {
+         cerr << "Error converting partitions to MBR; tried to convert "
+              << numToConvert << " partitions,\nbut converted " << numReallyConverted
+              << ". Aborting operation!\n";
+         numReallyConverted = 0;
+         protectiveMBR.MakeProtectiveMBR();
+      } // if/else
+   } // if
 
-   for (i = 0; i < (uint32_t) numParts; i++) {
-      j = partNums[i] - 1;
-      cout << "\nCreating entry for GPT partition #" << j + 1
-      << " (MBR partition #" << i + 1 << ")\n";
-      gptParts[i] = j;
-      mbrTypes[i] = GetMBRTypeCode(partitions[j].GetHexType() / 256);
-      cout << "Set the bootable flag? ";
-      if (GetYN() == 'Y')
-         bootable = i;
-      numConverted++;
-   } // for
-   numReallyConverted = PartsToMBR(gptParts, mbrTypes);
-   if (bootable > 0)
-      protectiveMBR.SetPartBootable(bootable);
-   if (numReallyConverted == numConverted) {
-      protectiveMBR.WriteMBRData(&myDisk);
+   // A problem or the user aborted; restore backup of unsorted and
+   // original-sized partition table and delete the sorted and
+   // resized one; otherwise free the backup table's memory....
+   if (numReallyConverted == 0) {
+      delete[] partitions;
+      partitions = tempGptParts;
+      SetGPTSize(origNumParts);
    } else {
-      cerr << "Error converting partitions to MBR; aborting operation!\n";
-      numConverted = 0;
-      protectiveMBR.MakeProtectiveMBR();
-   } // if/else
-   return numConverted;
+      delete[] tempGptParts;
+   } // if
+   return numReallyConverted;
 } // GPTDataTextUI::XFormToMBR()
 
 /*********************************************************************
diff --git a/gpttext.h b/gpttext.h
index ea4b3f5..f71e61b 100644
--- a/gpttext.h
+++ b/gpttext.h
@@ -47,6 +47,7 @@
       int DestroyGPTwPrompt(void); // Returns 1 if user proceeds
       void ShowDetails(void);
       void MakeHybrid(void);
+      int AssignPrimaryOrLogical(PartNotes& notes);
       int XFormToMBR(void); // convert GPT to MBR, wiping GPT afterwards. Returns 1 if successful
 }; // class GPTDataTextUI
 
diff --git a/guid.cc b/guid.cc
index 9c173ff..07fd733 100644
--- a/guid.cc
+++ b/guid.cc
@@ -61,7 +61,7 @@
    // Break points for segments, either with or without characters separating the segments....
    size_t longSegs[6] = {0, 9, 14, 19, 24, 36};
    size_t shortSegs[6] = {0, 8, 12, 16, 20, 32};
-   size_t *segStart = longSegs; // Assume there are separators between segments */
+   size_t *segStart = longSegs; // Assume there are separators between segments
 
    Zero();
 
@@ -150,7 +150,7 @@
       cout << "Enter a six-byte hexadecimal number for the fifth segment: ";
       cin >> part5;
       operator=(part1 += (string) "-" += part2 += (string) "-" += part3
-            += (string) "-" += part4 += (string) "-" += part5);
+                += (string) "-" += part4 += (string) "-" += part5);
    } // if/else
    cin.ignore(255, '\n');
    cout << "New GUID: " << AsString() << "\n";
@@ -217,15 +217,15 @@
 
 // Delete spaces or braces (which often enclose GUIDs) from the orig string,
 // returning modified string.
-string GUIDData::DeleteSpaces(const string & orig) {
-   string copy;
+string GUIDData::DeleteSpaces(string s) {
    size_t position;
 
-   copy = orig;
-   for (position = copy.length(); position > 0; position--) {
-      if ((copy[position - 1] == ' ') || (copy[position - 1] == '{') || (copy[position - 1] == '}')) {
-         copy.erase(position - 1, 1);
-      } // if
-   } // for
-   return copy;
+   if (s.length() > 0) {
+      for (position = s.length(); position > 0; position--) {
+         if ((s[position - 1] == ' ') || (s[position - 1] == '{') || (s[position - 1] == '}')) {
+            s.erase(position - 1, 1);
+         } // if
+      } // for
+   } // if
+   return s;
 } // GUIDData::DeleteSpaces()
diff --git a/guid.h b/guid.h
index 6fcf332..f5953ca 100644
--- a/guid.h
+++ b/guid.h
@@ -33,7 +33,7 @@
 class GUIDData {
    protected:
       my_uuid_t uuidData;
-      string DeleteSpaces(const string & orig);
+      string DeleteSpaces(string s);
    public:
       GUIDData(void);
       GUIDData(const GUIDData & orig);
diff --git a/mbr.cc b/mbr.cc
index 3f9b522..b142a7a 100644
--- a/mbr.cc
+++ b/mbr.cc
@@ -10,7 +10,6 @@
 #define __STDC_CONSTANT_MACROS
 
 #include <stdio.h>
-//#include <unistd.h>
 #include <stdlib.h>
 #include <stdint.h>
 #include <fcntl.h>
@@ -20,6 +19,7 @@
 #include <errno.h>
 #include <iostream>
 #include "mbr.h"
+#include "partnotes.h"
 #include "support.h"
 
 using namespace std;
@@ -182,7 +182,7 @@
          } // if
       } // if
 
-      /* Check to see if it's in GPT format.... */
+      // Check to see if it's in GPT format....
       if (allOK) {
          for (i = 0; i < 4; i++) {
             if (partitions[i].partitionType == UINT8_C(0xEE)) {
@@ -211,8 +211,7 @@
 // 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(uint32_t extendedStart,
-                             uint32_t diskOffset, int partNum) {
+int MBRData::ReadLogicalPart(uint32_t extendedStart, uint32_t diskOffset, int partNum) {
    struct TempMBR ebr;
    uint64_t offset;
 
@@ -267,8 +266,9 @@
    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.
+// Write the MBR data to the default defined device. This writes both the
+// MBR itself and any defined logical partitions, provided there's an
+// MBR extended partition.
 int MBRData::WriteMBRData(void) {
    int allOK = 1;
 
@@ -283,25 +283,16 @@
    return allOK;
 } // MBRData::WriteMBRData(void)
 
-// Save the MBR data to a file. Note that this function writes ONLY the
-// MBR data, not the logical partitions (if any are defined).
+// Save the MBR data to a file. This writes both the
+// MBR itself and any defined logical partitions, provided there's an
+// MBR extended partition.
 int MBRData::WriteMBRData(DiskIO *theDisk) {
-   int i, j, allOK;
+   int i, j, partNum, allOK, moreLogicals = 0;
+   uint32_t extFirstLBA = 0;
+   uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
    TempMBR tempMBR;
 
-   // Reverse the byte order, if necessary
-   if (IsLittleEndian() == 0) {
-      ReverseBytes(&diskSignature, 4);
-      ReverseBytes(&nulls, 2);
-      ReverseBytes(&MBRSignature, 2);
-      for (i = 0; i < 4; i++) {
-         ReverseBytes(&partitions[i].firstLBA, 4);
-         ReverseBytes(&partitions[i].lengthLBA, 4);
-      } // for
-   } // if
-
-   // Copy MBR data to a 512-byte data structure for writing, to
-   // work around a FreeBSD limitation....
+   // First write the main MBR data structure....
    for (i = 0; i < 440; i++)
       tempMBR.code[i] = code[i];
    tempMBR.diskSignature = diskSignature;
@@ -316,31 +307,49 @@
          tempMBR.partitions[i].firstSector[j] = partitions[i].firstSector[j];
          tempMBR.partitions[i].lastSector[j] = partitions[i].lastSector[j];
       } // for j...
-   } // for i...
-
-   // Now write that data structure...
-   allOK = theDisk->OpenForWrite();
-   if (allOK && theDisk->Seek(0)) {
-      if (theDisk->Write(&tempMBR, 512) != 512) {
-         allOK = 0;
-         cerr << "Warning! Error " << errno << " when saving MBR!\n";
+      if (partitions[i].partitionType == 0x0f) {
+         extFirstLBA = partitions[i].firstLBA;
+         moreLogicals = 1;
       } // if
-   } else {
-      allOK = 0;
-      cerr << "Warning! Error " << errno << " when seeking to MBR to write it!\n";
-   } // if/else
-   theDisk->Close();
+   } // for i...
+   allOK = WriteMBRData(tempMBR, theDisk, 0);
 
-   // Reverse the byte order back, if necessary
-   if (IsLittleEndian() == 0) {
-      ReverseBytes(&diskSignature, 4);
-      ReverseBytes(&nulls, 2);
-      ReverseBytes(&MBRSignature, 2);
-      for (i = 0; i < 4; i++) {
-         ReverseBytes(&partitions[i].firstLBA, 4);
-         ReverseBytes(&partitions[i].lengthLBA, 4);
-      } // for
-   }// if
+   // Set up tempMBR with some constant data for logical partitions...
+   tempMBR.diskSignature = 0;
+   for (i = 2; i < 4; i++) {
+      tempMBR.partitions[i].firstLBA = tempMBR.partitions[i].lengthLBA = 0;
+      tempMBR.partitions[i].partitionType = 0x00;
+      for (j = 0; j < 3; j++) {
+         tempMBR.partitions[i].firstSector[j] = 0;
+         tempMBR.partitions[i].lastSector[j] = 0;
+      } // for j
+   } // for i
+
+   partNum = 4;
+   writeEbrTo = (uint64_t) extFirstLBA;
+   // If extended partition is present, write logicals...
+   while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS)) {
+      tempMBR.partitions[0] = partitions[partNum];
+      tempMBR.partitions[0].firstLBA = 1; // partition starts on sector after EBR
+      // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
+      if ((partNum < MAX_MBR_PARTS - 1) && (partitions[partNum + 1].firstLBA > 0)) {
+         tempMBR.partitions[1].partitionType = 0x0f;
+         tempMBR.partitions[1].firstLBA = partitions[partNum + 1].firstLBA - 1;
+         tempMBR.partitions[1].lengthLBA = partitions[partNum + 1].lengthLBA + 1;
+         LBAtoCHS((uint64_t) tempMBR.partitions[1].firstLBA,
+                  (uint8_t *) &tempMBR.partitions[1].firstSector);
+         LBAtoCHS(tempMBR.partitions[1].lengthLBA - extFirstLBA,
+                  (uint8_t *) &tempMBR.partitions[1].lastSector);
+      } else {
+         tempMBR.partitions[1].partitionType = 0x00;
+         tempMBR.partitions[1].firstLBA = 0;
+         tempMBR.partitions[1].lengthLBA = 0;
+         moreLogicals = 0;
+      } // if/else
+      allOK = WriteMBRData(tempMBR, theDisk, writeEbrTo);
+      partNum++;
+      writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
+   } // while
    return allOK;
 } // MBRData::WriteMBRData(DiskIO *theDisk)
 
@@ -349,17 +358,64 @@
    return WriteMBRData();
 } // MBRData::WriteMBRData(const string & deviceFilename)
 
+// Write a single MBR record to the specified sector. Used by the like-named
+// function to write both the MBR and multiple EBR (for logical partition)
+// records.
+// Returns 1 on success, 0 on failure
+int MBRData::WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector) {
+   int i, allOK;
+
+   // Reverse the byte order, if necessary
+   if (IsLittleEndian() == 0) {
+      ReverseBytes(&mbr.diskSignature, 4);
+      ReverseBytes(&mbr.nulls, 2);
+      ReverseBytes(&mbr.MBRSignature, 2);
+      for (i = 0; i < 4; i++) {
+         ReverseBytes(&mbr.partitions[i].firstLBA, 4);
+         ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
+      } // for
+   } // if
+
+   // Now write the data structure...
+   allOK = theDisk->OpenForWrite();
+   if (allOK && theDisk->Seek(sector)) {
+      if (theDisk->Write(&mbr, 512) != 512) {
+         allOK = 0;
+         cerr << "Error " << errno << " when saving MBR!\n";
+      } // if
+   } else {
+      allOK = 0;
+      cerr << "Error " << errno << " when seeking to MBR to write it!\n";
+   } // if/else
+   theDisk->Close();
+
+   // Reverse the byte order back, if necessary
+   if (IsLittleEndian() == 0) {
+      ReverseBytes(&mbr.diskSignature, 4);
+      ReverseBytes(&mbr.nulls, 2);
+      ReverseBytes(&mbr.MBRSignature, 2);
+      for (i = 0; i < 4; i++) {
+         ReverseBytes(&mbr.partitions[i].firstLBA, 4);
+         ReverseBytes(&mbr.partitions[i].lengthLBA, 4);
+      } // for
+   }// if
+   return allOK;
+} // MBRData::WriteMBRData(uint64_t sector)
+
 /********************************************
  *                                          *
  * Functions that display data for the user *
  *                                          *
  ********************************************/
 
-// Show the MBR data to the user....
-void MBRData::DisplayMBRData(void) {
+// Show the MBR data to the user, up to the specified maximum number
+// of partitions....
+void MBRData::DisplayMBRData(int maxParts) {
    int i;
    char bootCode;
 
+   if (maxParts > MAX_MBR_PARTS)
+      maxParts = MAX_MBR_PARTS;
    cout << "MBR disk identifier: 0x";
    cout.width(8);
    cout.fill('0');
@@ -367,7 +423,7 @@
    cout << hex << diskSignature << dec << "\n";
    cout << "MBR partitions:\n";
    cout << "Number\t Boot\t Start (sector)\t Length (sectors)\tType\n";
-   for (i = 0; i < MAX_MBR_PARTS; i++) {
+   for (i = 0; i < maxParts; i++) {
       if (partitions[i].lengthLBA != 0) {
          if (partitions[i].status && 0x80) // it's bootable
             bootCode = '*';
@@ -462,7 +518,7 @@
          remainder -= head * numSecspTrack;
          sector = remainder;
          if (head < numHeads)
-            chs[0] = head;
+            chs[0] = (uint8_t) head;
          else
             retval = 0;
          if (sector < numSecspTrack) {
@@ -535,19 +591,6 @@
 
    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
-   // possible -- even to the point of exceeding the capacity of sub-8GB
-   // disks. The EFI spec says to use 0xffffff as the ending value,
-   // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
-   // and Apple's Disk Utility use 0xfeffff, and the latter puts that
-   // value in for the FIRST sector, too!
-   partitions[0].firstSector[0] = UINT8_C(0);
-   partitions[0].firstSector[1] = UINT8_C(1);
-   partitions[0].firstSector[2] = UINT8_C(0);
-   partitions[0].lastSector[0] = UINT8_C(255);
-   partitions[0].lastSector[1] = UINT8_C(255);
-   partitions[0].lastSector[2] = UINT8_C(255);
-
    partitions[0].partitionType = UINT8_C(0xEE);
    partitions[0].firstLBA = UINT32_C(1);
    if (diskSize < UINT32_MAX) { // If the disk is under 2TiB
@@ -556,6 +599,16 @@
       partitions[0].lengthLBA = UINT32_MAX;
    } // if/else
 
+   // Write CHS data. This maxes out the use of the disk, as much as
+   // possible -- even to the point of exceeding the capacity of sub-8GB
+   // disks. The EFI spec says to use 0xffffff as the ending value,
+   // although normal MBR disks max out at 0xfeffff. FWIW, both GNU Parted
+   // and Apple's Disk Utility use 0xfeffff, and the latter puts that
+   // value in for the FIRST sector, too!
+   LBAtoCHS(1, partitions[0].firstSector);
+   if (LBAtoCHS(partitions[0].lengthLBA, partitions[0].lastSector) == 0)
+      partitions[0].lastSector[0] = 0xFF;
+
    state = gpt;
 } // MBRData::MakeProtectiveMBR()
 
@@ -567,10 +620,8 @@
 // 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) {
+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);
@@ -715,6 +766,56 @@
    } // if
 } // MBRData::OptimizeEESize()
 
+// Creates an MBR extended partition holding logical partitions that
+// correspond to the list of GPT partitions in theList. The extended
+// partition is placed in position #4 (counting from 1) in the MBR.
+// The logical partition data are copied to the partitions[] array in
+// positions 4 and up (counting from 0). Neither the MBR nor the EBR
+// entries are written to disk; that is left for the WriteMBRData()
+// function.
+// Returns number of converted partitions
+int MBRData::CreateLogicals(PartNotes& notes) {
+   uint64_t extEndLBA = 0, extStartLBA = UINT64_MAX;
+   int i = 4, numLogicals = 0;
+   struct PartInfo aPart;
+
+   // Find bounds of the extended partition....
+   notes.Rewind();
+   while (notes.GetNextInfo(&aPart) >= 0) {
+      if (aPart.type == LOGICAL) {
+         if (extStartLBA > aPart.firstLBA)
+            extStartLBA = aPart.firstLBA;
+         if (extEndLBA < aPart.lastLBA)
+            extEndLBA = aPart.lastLBA;
+         numLogicals++;
+      } // if
+   } // while
+   extStartLBA--;
+
+   if ((extStartLBA < UINT32_MAX) && ((extEndLBA - extStartLBA + 1) < UINT32_MAX)) {
+      notes.Rewind();
+      i = 4;
+      while ((notes.GetNextInfo(&aPart) >= 0) && (i < MAX_MBR_PARTS)) {
+         if (aPart.type == LOGICAL) {
+            partitions[i].partitionType = aPart.hexCode;
+            partitions[i].firstLBA = (uint32_t) (aPart.firstLBA - extStartLBA);
+            partitions[i].lengthLBA = (uint32_t) (aPart.lastLBA - aPart.firstLBA + 1);
+            LBAtoCHS(UINT64_C(1), (uint8_t *) &partitions[i].firstSector);
+            LBAtoCHS(partitions[i].lengthLBA, (uint8_t *) &partitions[i].lastSector);
+            partitions[i].status = aPart.active * 0x80;
+            i++;
+         } // if
+      } // while
+      MakePart(3, (uint32_t) extStartLBA, (uint32_t) (extEndLBA - extStartLBA + 1), 0x0f, 0);
+   } else {
+      if (numLogicals > 0) {
+         cerr << "Unable to create logical partitions; they exceed the 2 TiB limit!\n";
+//         cout << "extStartLBA = " << extStartLBA << ", extEndLBA = " << extEndLBA << "\n";
+      }
+   } // if/else
+   return (i - 4);
+} // MBRData::CreateLogicals()
+
 /****************************************
  *                                      *
  * Functions to find data on free space *
@@ -777,10 +878,10 @@
    bestLastLBA = 1;
    for (i = 0; i < 4; i++) {
       thisLastLBA = partitions[i].firstLBA + partitions[i].lengthLBA;
-      if (thisLastLBA > 0) thisLastLBA--;
-      if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start)) {
+      if (thisLastLBA > 0)
+         thisLastLBA--;
+      if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
          bestLastLBA = thisLastLBA + 1;
-      } // if
    } // for
    return (bestLastLBA);
 } // MBRData::FindFirstInFree()
@@ -796,7 +897,8 @@
       // 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 (last > 0)
+         last--;
       if ((first <= sector) && (last >= sector))
          isFree = 0;
    } // for
diff --git a/mbr.h b/mbr.h
index 01cabcb..1e838a6 100644
--- a/mbr.h
+++ b/mbr.h
@@ -6,6 +6,7 @@
 #include <stdint.h>
 #include <sys/types.h>
 #include "gptpart.h"
+#include "partnotes.h"
 #include "diskio.h"
 
 #ifndef __MBRSTRUCTS
@@ -21,6 +22,8 @@
 
 using namespace std;
 
+class PartNotes;
+
 /****************************************
  *                                      *
  * MBRData class and related structures *
@@ -30,13 +33,17 @@
 // Data for a single MBR partition record
 // Note that firstSector and lastSector are in CHS addressing, which
 // splits the bits up in a weird way.
+// On read or write of MBR entries, firstLBA is an absolute disk sector.
+// On read of logical entries, it's relative to the EBR record for that
+// partition. When writing EBR records, it's relative to the extended
+// partition's start.
 #pragma pack(1)
 struct MBRRecord {
    uint8_t status;
    uint8_t firstSector[3];
    uint8_t partitionType;
    uint8_t lastSector[3];
-   uint32_t firstLBA;
+   uint32_t firstLBA; // see above
    uint32_t lengthLBA;
 }; // struct MBRRecord
 
@@ -90,9 +97,11 @@
    int WriteMBRData(void);
    int WriteMBRData(DiskIO *theDisk);
    int WriteMBRData(const string & deviceFilename);
+   int WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector);
+   void SetDisk(DiskIO *theDisk) {myDisk = theDisk; canDeleteMyDisk = 0;}
 
    // Display data for user...
-   void DisplayMBRData(void);
+   void DisplayMBRData(int maxParts = 4);
    void ShowState(void);
 
    // Functions that set or get disk metadata (size, CHS geometry, etc.)
@@ -115,6 +124,7 @@
    void DeletePartition(int i);
    int DeleteByLocation(uint64_t start64, uint64_t length64);
    void OptimizeEESize(void);
+   int CreateLogicals(PartNotes& notes);
 
    // Functions to find information on free space....
    uint32_t FindFirstAvailable(uint32_t start = 1);
diff --git a/parttypes.cc b/parttypes.cc
index 1bda724..968be74 100644
--- a/parttypes.cc
+++ b/parttypes.cc
@@ -29,8 +29,6 @@
 // related codes similar numbers and (given appropriate entry positions
 // in the linked list) keeps them together in the listings generated
 // by typing "L" at the main gdisk menu.
-// See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
-// for a list of MBR partition type codes.
 PartType::PartType(void) : GUIDData() {
    numInstances++;
    if (numInstances == 1) {
@@ -67,6 +65,8 @@
 
 // Add all partition type codes to the internal linked-list structure.
 // Used by constructors.
+// See http://www.win.tue.nl/~aeb/partitions/partition_types-1.html
+// for a list of MBR partition type codes.
 void PartType::AddAllTypes(void) {
    // Start with the "unused entry," which should normally appear only
    // on empty partition table entries....
@@ -161,7 +161,7 @@
    AddType(0xfd00, "A19D880F-05FC-4D3B-A006-743F0F84911E", "Linux RAID");
 
    // Note: DO NOT use the 0xffff code; that's reserved to indicate an
-   // unknown type code.
+   // unknown GUID type code.
 } // PartType::AddAllTypes()
 
 // Add a single type to the linked list of types. Returns 1 if operation
@@ -195,12 +195,9 @@
    AType* theItem = allTypes;
    int found = 0;
 
-   // Assign a default value....
-   GUIDData::operator=("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"); // 0700, Linux/Windows data
-
    // Now search the type list for a match to the ID....
    while ((theItem != NULL) && (!found)) {
-      if (theItem->MBRType == ID)  { // found it!
+      if (theItem->MBRType == ID)  {
          GUIDData::operator=(theItem->GUIDType);
          found = 1;
       } else {
@@ -208,6 +205,8 @@
       } // if/else
    } // while
    if (!found) {
+      // Assign a default value....
+      GUIDData::operator=("EBD0A0A2-B9E5-4433-87C0-68B6B72699C7"); // 0700, Linux/Windows data
       cout.setf(ios::uppercase);
       cout.fill('0');
       cout << "Exact type match not found for type code ";
@@ -278,7 +277,8 @@
          cout.width(4);
          cout << hex << thisType->MBRType << " ";
          cout << thisType->name.substr(0, 20);
-         for (i = 0; i < (20 - (thisType->name.substr(0, 20).length())); i++) cout << " ";
+         for (i = 0; i < (20 - (thisType->name.substr(0, 20).length())); i++)
+            cout << " ";
          if ((colCount % 3) == 0)
             cout << "\n";
          else
diff --git a/parttypes.h b/parttypes.h
index 4a40ecd..bff92c1 100644
--- a/parttypes.h
+++ b/parttypes.h
@@ -2,7 +2,6 @@
   under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
 
 #include <stdint.h>
-//#include <unistd.h>
 #include <stdlib.h>
 #include <string>
 #include "support.h"
diff --git a/sgdisk.8 b/sgdisk.8
index 28a2c98..64516da 100644
--- a/sgdisk.8
+++ b/sgdisk.8
@@ -1,6 +1,6 @@
-.\" Copyright 2009 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" Copyright 2010 Roderick W. Smith (rodsmith@rodsbooks.com)
 .\" May be distributed under the GNU General Public License
-.TH "SGDISK" "8" "0.6.4" "Roderick W. Smith" "GPT fdisk Manual"
+.TH "SGDISK" "8" "0.6.5" "Roderick W. Smith" "GPT fdisk Manual"
 .SH "NAME"
 sgdisk \- Command\-line GUID partition table (GPT) manipulator for Linux and Unix
 .SH "SYNOPSIS"
@@ -185,6 +185,12 @@
 MBR protective partition to fill the new free space.
 
 .TP 
+.B \-D, \-\-display\-alignment
+Display current sector alignment value. Partitions will be created on multiples
+of the sector value reported by this option. You can change the alignment value
+with the \-a option.
+
+.TP 
 .B e, \-\-move\-second\-header
 Move backup GPT data structures to the end of the disk. Use this option if
 you've added disks to a RAID array, thus creating a virtual disk with space
@@ -211,7 +217,7 @@
 this option is required on MBR or BSD disklabel disks if you intend to save your
 changes, in order to prevent accidentally damaging such disks.
 
-.TP
+.TP 
 .B \-h, \-\-hybrid
 Create a hybrid MBR. This option takes from one to three partition numbers,
 separated by colons, as arguments. The created hybrid MBR places an EFI GPT
@@ -253,7 +259,7 @@
 these two\-byte codes are unique to \fBgdisk\fR and \fBsgdisk\fR. This
 option does not require you to specify a valid disk device filename.
 
-.TP
+.TP 
 .B \-m, \-\-gpttombr
 Convert disk from GPT to MBR form. This option takes from one to four
 partition numbers, separated by colons, as arguments. Their type codes are
@@ -297,7 +303,7 @@
 altered according to other parameters, but changes are not written
 to disk.
 
-.TP
+.TP 
 .B \-r, \-\-transpose
 Swap two partitions' entries in the partition table. One or both partitions
 may be empty, although swapping two empty partitions is pointless. For
@@ -353,13 +359,13 @@
 want to repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware
 program. This option destroys only the GPT data structures; it leaves the
 MBR intact. This makes it useful for wiping out GPT data structures after a
-disk has been repartitioned for MBR using a GPT-unaware utility; however,
+disk has been repartitioned for MBR using a GPT\-unaware utility; however,
 there's a risk that it will damage boot loaders or even the start of the
 first or end of the last MBR partition. If you use it on a valid GPT disk,
 the MBR will be left with an inappropriate EFI GPT (0xEE) partition
 definition, which you can delete using another utility.
 
-.TP
+.TP 
 .B \-Z, \-\-zap\-all
 Zap (destroy) the GPT and MBR data structures and then exit. This option
 works much like \fI\-z\fR, but as it wipes the MBR as well as the GPT, it's
@@ -393,7 +399,7 @@
 .B 4
 An error prevented saving changes
 .SH "BUGS"
-As of February 2010 (version 0.6.4), \fBsgdisk\fR
+As of March 2010 (version 0.6.5), \fBsgdisk\fR
 should be considered beta software. Known bugs and limitations include:
 
 .TP 
diff --git a/sgdisk.cc b/sgdisk.cc
index b4da1e0..911f0c2 100644
--- a/sgdisk.cc
+++ b/sgdisk.cc
@@ -14,6 +14,7 @@
 #include <popt.h>
 #include <errno.h>
 #include <iostream>
+#include <sstream>
 #include "mbr.h"
 #include "gpt.h"
 #include "support.h"
@@ -48,6 +49,7 @@
       {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"},
       {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"},
       {"delete", 'd', POPT_ARG_INT, &deletePartNum, 'd', "delete a partition", "partnum"},
+      {"display-alignment", 'D', POPT_ARG_NONE, NULL, 'D', "show number of sectors per allocation block", ""},
       {"move-second-header", 'e', POPT_ARG_NONE, NULL, 'e', "move second header to end of disk", ""},
       {"end-of-largest", 'E', POPT_ARG_NONE, NULL, 'E', "show end of largest free block", ""},
       {"first-in-largest", 'f', POPT_ARG_NONE, NULL, 'f', "show start of the largest free block", ""},
@@ -140,6 +142,10 @@
                      neverSaveData = 1;
                   } else saveData = 1;
                   break;
+               case 'D':
+                  cout << "Partitions created on multiples of " << theGPT.GetAlignment()
+                       << " sector(s)\n";
+                  break;
                case 'e':
                   theGPT.JustLooking(0);
                   theGPT.MoveSecondHeaderToEnd();
@@ -299,7 +305,7 @@
 // Extract integer data from argument string, which should be colon-delimited
 uint64_t GetInt(char* argument, int itemNum) {
    int startPos = -1, endPos = -1;
-   unsigned long long retval = 0;
+   uint64_t retval = 0;
    string Info;
 
    Info = argument;
@@ -311,7 +317,8 @@
       endPos = Info.length();
    endPos--;
 
-   sscanf(Info.substr(startPos, endPos - startPos + 1).c_str(), "%llu", &retval);
+   istringstream inString(Info.substr(startPos, endPos - startPos + 1));
+   inString >> retval;
    return retval;
 } // GetInt()
 
@@ -335,23 +342,32 @@
 // Create a hybrid or regular MBR from GPT data structures
 int BuildMBR(GPTData* theGPT, char* argument, int isHybrid) {
    int numParts, allOK = 1, i;
-   int gptParts[4], mbrTypes[4];
+   PartNotes notes;
+   struct PartInfo *newNote;
 
-   for (i = 0; i < 4; i++) {
-      gptParts[i] = MBR_EMPTY;
-      mbrTypes[i] = 0; // All 0s flags to use default type
-   } // for
-   if ((theGPT != NULL) && (argument != NULL) && ((isHybrid == 0) || (isHybrid == 1))) {
+   if ((theGPT != NULL) && (argument != NULL)) {
       numParts = CountColons(argument) + 1;
       if (numParts <= (4 - isHybrid)) {
-         if (isHybrid) {
-            gptParts[0] = MBR_EFI_GPT;
-            mbrTypes[0] = 0xEE;
-         } // if
          for (i = 0; i < numParts; i++) {
-            gptParts[i + isHybrid] = GetInt(argument, i + 1) - 1;
+            newNote = new struct PartInfo;
+            newNote->gptPartNum = GetInt(argument, i + 1) - 1;
+            newNote->active = 0;
+            newNote->hexCode = 0; // code to compute it from default
+            newNote->type = PRIMARY;
+            newNote->firstLBA = theGPT->GetPartFirstLBA(newNote->gptPartNum);
+            newNote->lastLBA = theGPT->GetPartLastLBA(newNote->gptPartNum);
+            notes.AddToEnd(newNote);
          } // for
-         if (theGPT->PartsToMBR(gptParts, mbrTypes) != numParts)
+         if (isHybrid) {
+            newNote = new struct PartInfo;
+            newNote->gptPartNum = MBR_EFI_GPT;
+            newNote->active = 0;
+            newNote->hexCode = 0xEE;
+            newNote->type = PRIMARY;
+            // newNote firstLBA and lastLBA are computed later...
+            notes.AddToStart(newNote);
+         } // if
+         if (theGPT->PartsToMBR(notes) != numParts)
             allOK = 0;
       } else allOK = 0;
    } else allOK = 0;
diff --git a/support.cc b/support.cc
index 168262e..ab8f767 100644
--- a/support.cc
+++ b/support.cc
@@ -84,11 +84,9 @@
 // specified number of sectors (or KiB, MiB, etc.). Use the def
  //value as the default if the user just hits Enter
 uint64_t GetSectorNum(uint64_t low, uint64_t high, uint64_t def, const string & prompt) {
-   unsigned long long response;
-   int num, plusFlag = 0;
-   uint64_t mult = 1;
-   char suffix;
-   char line[255];
+   uint64_t response, mult = 1;
+   int plusFlag = 0;
+   char suffix, line[255];
 
    response = low - 1; // Ensure one pass by setting a too-low initial value
    while ((response < low) || (response > high)) {
@@ -112,12 +110,13 @@
       } // if
 
       // Extract numeric response and, if present, suffix
-      num = sscanf(line, "%llu%c", &response, &suffix);
+      istringstream inString(line);
+      inString >> response >> suffix;
 
       // If no response, use default (def)
-      if (num <= 0) {
-         response = (unsigned long long) def;
-	 suffix = ' ';
+      if (strlen(line) == 0) {
+         response = def;
+	      suffix = ' ';
          plusFlag = 0;
       } // if
 
@@ -144,22 +143,22 @@
       } // switch
 
       // Adjust response based on multiplier and plus flag, if present
-      response *= (unsigned long long) mult;
+      response *= mult;
       if (plusFlag == 1) {
          // Recompute response based on low part of range (if default = high
          // value, which should be the case when prompting for the end of a
          // range) or the defaut value (if default != high, which should be
          // the case for the first sector of a partition).
          if (def == high)
-            response = response + (unsigned long long) low - UINT64_C(1);
+            response = response + low - UINT64_C(1);
          else
-            response = response + (unsigned long long) def - UINT64_C(1);
+            response = response + def - UINT64_C(1);
       } // if
       if (plusFlag == -1) {
-         response = (unsigned long long) high - response;
+         response = high - response;
       } // if
    } // while
-   return ((uint64_t) response);
+   return response;
 } // GetSectorNum()
 
 // Takes a size in bytes (in size) and converts this to a size in