Project import
diff --git a/gptfdisk/Android.mk b/gptfdisk/Android.mk
new file mode 100644
index 0000000..245bd78
--- /dev/null
+++ b/gptfdisk/Android.mk
@@ -0,0 +1,52 @@
+LOCAL_PATH := $(call my-dir)
+
+sgdisk_src_files := \
+    sgdisk.cc \
+    gptcl.cc \
+    crc32.cc \
+    support.cc \
+    guid.cc \
+    gptpart.cc \
+    mbrpart.cc \
+    basicmbr.cc \
+    mbr.cc \
+    gpt.cc \
+    bsd.cc \
+    parttypes.cc \
+    attributes.cc \
+    diskio.cc \
+    diskio-unix.cc \
+    android_popt.cc \
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH) external/e2fsprogs/lib
+LOCAL_SRC_FILES := $(sgdisk_src_files)
+LOCAL_CFLAGS += -Wno-unused-parameter -Werror
+
+LOCAL_SHARED_LIBRARIES := libext2_uuid
+
+LOCAL_MODULE := sgdisk
+
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+
+LOCAL_CPP_EXTENSION := .cc
+
+ifeq ($(HOST_OS),darwin)
+LOCAL_CFLAGS := -D_FILE_OFFSET_BITS=64 -Doff64_t=off_t
+endif
+
+LOCAL_C_INCLUDES := $(LOCAL_PATH) external/e2fsprogs/lib
+LOCAL_SRC_FILES := $(sgdisk_src_files)
+LOCAL_CFLAGS += -Wno-unused-parameter -Werror
+
+LOCAL_SHARED_LIBRARIES := libext2_uuid-host
+
+LOCAL_MODULE := sgdisk_host
+LOCAL_MODULE_STEM := sgdisk
+
+include $(BUILD_HOST_EXECUTABLE)
diff --git a/gptfdisk/COPYING b/gptfdisk/COPYING
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/gptfdisk/COPYING
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gptfdisk/MODULE_LICENSE_GPL b/gptfdisk/MODULE_LICENSE_GPL
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gptfdisk/MODULE_LICENSE_GPL
diff --git a/gptfdisk/Makefile b/gptfdisk/Makefile
new file mode 100644
index 0000000..8c8f9fd
--- /dev/null
+++ b/gptfdisk/Makefile
@@ -0,0 +1,47 @@
+CC=gcc
+CXX=g++
+CFLAGS+=-D_FILE_OFFSET_BITS=64
+#CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16
+CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64
+LDFLAGS+=
+LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix
+MBR_LIBS=support diskio diskio-unix basicmbr mbrpart
+LIB_OBJS=$(LIB_NAMES:=.o)
+MBR_LIB_OBJS=$(MBR_LIBS:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CXXFLAGS)
+
+all:	cgdisk gdisk sgdisk fixparts
+
+gdisk:	$(LIB_OBJS) gdisk.o gpttext.o
+	$(CXX) $(LIB_OBJS) gdisk.o gpttext.o $(LDFLAGS) -luuid -o gdisk
+#	$(CXX) $(LIB_OBJS) gdisk.o gpttext.o $(LDFLAGS) -licuio -licuuc -luuid -o gdisk
+
+cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o
+	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -luuid -lncursesw -o cgdisk
+#	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -licuio -licuuc -luuid -lncurses -o cgdisk
+
+sgdisk: $(LIB_OBJS) sgdisk.o gptcl.o
+	$(CXX) $(LIB_OBJS) sgdisk.o gptcl.o $(LDFLAGS) -luuid -lpopt -o sgdisk
+#	$(CXX) $(LIB_OBJS) sgdisk.o gptcl.o $(LDFLAGS) -licuio -licuuc -luuid -lpopt -o sgdisk
+
+fixparts: $(MBR_LIB_OBJS) fixparts.o
+	$(CXX) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) -o fixparts
+
+test:
+	./gdisk_test.sh
+
+lint:	#no pre-reqs
+	lint $(SRCS)
+
+clean:	#no pre-reqs
+	rm -f core *.o *~ gdisk sgdisk cgdisk fixparts
+
+# what are the source dependencies
+depend: $(SRCS)
+	$(DEPEND) $(SRCS)
+
+$(OBJS):
+	$(CRITICAL_CXX_FLAGS) 
+
+# DO NOT DELETE
diff --git a/gptfdisk/Makefile.freebsd b/gptfdisk/Makefile.freebsd
new file mode 100644
index 0000000..dace733
--- /dev/null
+++ b/gptfdisk/Makefile.freebsd
@@ -0,0 +1,44 @@
+CC=gcc
+CXX=g++
+CFLAGS+=-D_FILE_OFFSET_BITS=64
+#CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16 -I/usr/local/include
+CXXFLAGS+=-Wall -D_FILE_OFFSET_BITS=64 -I /usr/local/include 
+LDFLAGS+=
+LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix
+MBR_LIBS=support diskio diskio-unix basicmbr mbrpart
+LIB_OBJS=$(LIB_NAMES:=.o)
+MBR_LIB_OBJS=$(MBR_LIBS:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CXXFLAGS)
+
+all:	gdisk cgdisk sgdisk fixparts
+
+gdisk:	$(LIB_OBJS) gdisk.o gpttext.o
+#	$(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/usr/local/lib $(LDFLAGS) -licuio -luuid -o gdisk
+	$(CXX) $(LIB_OBJS) gdisk.o gpttext.o -L/usr/local/lib $(LDFLAGS) -luuid -o gdisk
+
+cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o
+#	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o -L/usr/local/lib $(LDFLAGS) -licuio -luuid -lncurses -o cgdisk
+	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o -L/usr/local/lib $(LDFLAGS) -luuid -lncurses -o cgdisk
+
+sgdisk: $(LIB_OBJS) sgdisk.o gptcl.o
+#	$(CXX) $(LIB_OBJS) sgdisk.o gptcl.o -L/usr/local/lib $(LDFLAGS) -luuid -licuio -lpopt -o sgdisk
+	$(CXX) $(LIB_OBJS) sgdisk.o gptcl.o -L/usr/local/lib $(LDFLAGS) -luuid -lpopt -o sgdisk
+
+fixparts: $(MBR_LIB_OBJS) fixparts.o
+	$(CXX) $(MBR_LIB_OBJS) fixparts.o -L/usr/local/lib $(LDFLAGS) -o fixparts
+
+lint:	#no pre-reqs
+	lint $(SRCS)
+
+clean:	#no pre-reqs
+	rm -f core *.o *~ gdisk sgdisk
+
+# what are the source dependencies
+depend: $(SRCS)
+	$(DEPEND) $(SRCS)
+
+$(OBJS):
+	$(CRITICAL_CXX_FLAGS) 
+
+# DO NOT DELETE
diff --git a/gptfdisk/Makefile.mac b/gptfdisk/Makefile.mac
new file mode 100644
index 0000000..ac5e773
--- /dev/null
+++ b/gptfdisk/Makefile.mac
@@ -0,0 +1,48 @@
+CC=gcc
+CXX=clang++
+FATBINFLAGS=-arch x86_64 -arch i386 -mmacosx-version-min=10.4
+CFLAGS=$(FATBINFLAGS) -O2 -D_FILE_OFFSET_BITS=64 -g
+#CXXFLAGS=-O2 -Wall -D_FILE_OFFSET_BITS=64 -D USE_UTF16 -I/opt/local/include -I/usr/local/include -I/opt/local/include -g
+CXXFLAGS=$(FATBINFLAGS) -O2 -Wall -D_FILE_OFFSET_BITS=64 -I/opt/local/include -I /usr/local/include -I/opt/local/include -g
+LIB_NAMES=crc32 support guid gptpart mbrpart basicmbr mbr gpt bsd parttypes attributes diskio diskio-unix
+MBR_LIBS=support diskio diskio-unix basicmbr mbrpart
+#LIB_SRCS=$(NAMES:=.cc)
+LIB_OBJS=$(LIB_NAMES:=.o)
+MBR_LIB_OBJS=$(MBR_LIBS:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CFLAGS)
+
+all:	gdisk sgdisk cgdisk fixparts
+
+gdisk:	$(LIB_OBJS) gpttext.o gdisk.o
+	$(CXX) $(LIB_OBJS) gpttext.o gdisk.o $(FATBINFLAGS) -o gdisk
+#	$(CXX) $(LIB_OBJS) -L/usr/lib -licucore gpttext.o gdisk.o -o gdisk
+
+cgdisk: $(LIB_OBJS) cgdisk.o gptcurses.o
+	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o /opt/local/lib/libncurses.a $(LDFLAGS) $(FATBINFLAGS) -o cgdisk
+#	$(CXX) $(LIB_OBJS) cgdisk.o gptcurses.o $(LDFLAGS) -licucore -lncurses -o cgdisk
+
+sgdisk: $(LIB_OBJS) gptcl.o sgdisk.o
+#	$(CXX) $(LIB_OBJS) gptcl.o sgdisk.o /opt/local/lib/libiconv.a /opt/local/lib/libintl.a /opt/local/lib/libpopt.a $(FATBINFLAGS) -o sgdisk
+	$(CXX) $(LIB_OBJS) gptcl.o sgdisk.o -L/opt/local/lib -lpopt $(FATBINFLAGS) -o sgdisk
+#	$(CXX) $(LIB_OBJS) gptcl.o sgdisk.o -L/sw/lib -licucore -lpopt -o sgdisk
+
+fixparts: $(MBR_LIB_OBJS) fixparts.o
+	$(CXX) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) $(FATBINFLAGS) -o fixparts
+
+testguid:	$(LIB_OBJS) testguid.o
+	$(CXX) $(LIB_OBJS) testguid.o -o testguid
+
+lint:	#no pre-reqs
+	lint $(SRCS)
+
+clean:	#no pre-reqs
+	rm -f core *.o *~ gdisk sgdisk cgdisk fixparts
+
+# what are the source dependencies
+depend: $(SRCS)
+	$(DEPEND) $(SRCS)
+
+$(OBJS):
+
+# DO NOT DELETE
diff --git a/gptfdisk/Makefile.mingw b/gptfdisk/Makefile.mingw
new file mode 100644
index 0000000..acfff64
--- /dev/null
+++ b/gptfdisk/Makefile.mingw
@@ -0,0 +1,41 @@
+CC=/usr/bin/i686-w64-mingw32-gcc
+CXX=/usr/bin/i686-w64-mingw32-g++
+STRIP=/usr/bin/i686-w64-mingw32-strip
+CFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++  -D_FILE_OFFSET_BITS=64 -g
+CXXFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -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 mbrpart basicmbr mbr gpt support diskio diskio-windows
+MBR_LIBS=support diskio diskio-windows basicmbr mbrpart
+LIB_SRCS=$(NAMES:=.cc)
+LIB_OBJS=$(LIB_NAMES:=.o)
+MBR_LIB_OBJS=$(MBR_LIBS:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CFLAGS)
+
+all:	gdisk fixparts
+
+gdisk:	$(LIB_OBJS) gdisk.o gpttext.o
+	$(CXX) $(CXXFLAGS) $(LIB_OBJS) gdisk.o gpttext.o -lrpcrt4 -static-libgcc -o gdisk32.exe
+
+sgdisk: $(LIB_OBJS) sgdisk.o
+	$(CXX) $(CXXFLAGS) $(LIB_OBJS) sgdisk.o -lpopt -static-libgcc -o sgdisk32.exe
+
+fixparts: $(MBR_LIB_OBJS) fixparts.o
+	$(CXX) $(CXXFLAGS) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) -static-libgcc -o fixparts32.exe
+
+lint:	#no pre-reqs
+	lint $(SRCS)
+
+clean:	#no pre-reqs
+	rm -f core *.o *~ gdisk.exe sgdisk.exe
+
+strip:	#no pre-reqs
+	$(STRIP) gdisk32.exe fixparts32.exe
+
+# what are the source dependencies
+depend: $(SRCS)
+	$(DEPEND) $(SRCS)
+
+$(OBJS):
+
+# DO NOT DELETE
diff --git a/gptfdisk/Makefile.mingw64 b/gptfdisk/Makefile.mingw64
new file mode 100644
index 0000000..7e4b32b
--- /dev/null
+++ b/gptfdisk/Makefile.mingw64
@@ -0,0 +1,41 @@
+CC=/usr/bin/x86_64-w64-mingw32-gcc
+CXX=/usr/bin/x86_64-w64-mingw32-g++
+STRIP=/usr/bin/x86_64-w64-mingw32-strip
+CFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++  -D_FILE_OFFSET_BITS=64 -g
+CXXFLAGS=-O2 -Wall -static -static-libgcc -static-libstdc++ -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 mbrpart basicmbr mbr gpt support diskio diskio-windows
+MBR_LIBS=support diskio diskio-windows basicmbr mbrpart
+LIB_SRCS=$(NAMES:=.cc)
+LIB_OBJS=$(LIB_NAMES:=.o)
+MBR_LIB_OBJS=$(MBR_LIBS:=.o)
+LIB_HEADERS=$(LIB_NAMES:=.h)
+DEPEND= makedepend $(CFLAGS)
+
+all:	gdisk fixparts
+
+gdisk:	$(LIB_OBJS) gdisk.o gpttext.o
+	$(CXX) $(CXXFLAGS) $(LIB_OBJS) gdisk.o gpttext.o -lrpcrt4 -static-libgcc -o gdisk64.exe
+
+sgdisk: $(LIB_OBJS) sgdisk.o
+	$(CXX) $(CXXFLAGS) $(LIB_OBJS) sgdisk.o -lpopt -static-libgcc -o sgdisk64.exe
+
+fixparts: $(MBR_LIB_OBJS) fixparts.o
+	$(CXX) $(CXXFLAGS) $(MBR_LIB_OBJS) fixparts.o $(LDFLAGS) -static-libgcc -o fixparts64.exe
+
+lint:	#no pre-reqs
+	lint $(SRCS)
+
+clean:	#no pre-reqs
+	rm -f core *.o *~ gdisk64.exe sgdisk64.exe
+
+strip:	#no pre-reqs
+	$(STRIP) gdisk64.exe fixparts64.exe
+
+# what are the source dependencies
+depend: $(SRCS)
+	$(DEPEND) $(SRCS)
+
+$(OBJS):
+
+# DO NOT DELETE
diff --git a/gptfdisk/NEWS b/gptfdisk/NEWS
new file mode 100644
index 0000000..8835391
--- /dev/null
+++ b/gptfdisk/NEWS
@@ -0,0 +1,1259 @@
+1.0.1 (10/18/2015):
+-------------------
+
+- Fixed bug that caused -N/--largest-new option to sgdisk to fail when
+  fed a "0" option.
+
+- Fixed bug that caused input glitches in EFI version of gdisk.
+
+- Fixed bug that caused sgdisk to not return an appropriate error code
+  when it encountered a write error when saving changes.
+
+- Fixed bug that caused cgdisk's "Info" display to under-report the
+  partition's size by one sector.
+
+- OS X 10.11 includes new security features that prevent GPT fdisk from
+  working unless these features are disabled. To do so, you must boot to a
+  Recovery HD system, open a Terminal, type "csrutil disable", and reboot
+  into the normal system. You can re-enable the security features by
+  repeating the process, but specify "enable" rather than "disable". I've
+  added a message pointing users to a Web page explaining how to disable
+  this feature when gdisk detects that it can't write to the disk under OS
+  X. If you know of a way around this (including code changes to gdisk),
+  please contact me.
+
+- I've updated the OS X installation location from the Unix-standard
+  /usr/sbin (and related locations for documentation) to /usr/local/bin
+  (and related locations for documentation). This is Just Plain Crazy from
+  a Unix point of view, but Apple has to be Apple and do things just a
+  little bit differently.
+
+- I've updated my OS X environment to OS X 10.11 and LLVM 7.0.0. This has
+  also meant installing fresh versions of popt and ncurses from MacPorts,
+  which may require upgrading popt to get sgdisk working on some systems.
+  (gdisk, cgdisk, and fixparts should continue to work normally on all
+  systems.) The OS X binaries are now "fat" (32- and 64-bit) versions,
+  which should have no noticeable effect unless you have a Mac with broken
+  32-bit support, in which case the binaries will now work.
+
+- Changed the default name of 0xab00 partitions from "Apple boot" to
+  "Recovery HD", since the latter is the name that Apple gives these
+  partitions. Also, I discovered through painful experience that OS X
+  flakes out and won't boot if the name is something other than "Recovery
+  HD", so it really has to have the right name!
+
+- Changed the OpenBSD type codes (0xa600 and 0xa601): 0xa600 is now
+  824CC7A0-36A8-11E3-890A-952519AD3F61 (OpenBSD disklabel) and 0xa601 is
+  now gone. Previously, 0xa600 was 516E7CB4-6ECF-11D6-8FF8-00022D09712B, a
+  duplicate of the FreeBSD disklabel, and 0xa601 was
+  824CC7A0-36A8-11E3-890A-952519AD3F61. OpenBSD is now officially
+  supporting 824CC7A0-36A8-11E3-890A-952519AD3F61 as a disklabel type,
+  though. It's unclear what, if anything, OpenBSD will use for
+  non-disklabel type codes at the moment.
+
+- Added GUID 0311FC50-01CA-4725-AD77-9ADBB20ACE98 (0xbc00) for
+  Acronis Secure Zone backup partitions.
+
+- Fixed bug that caused random crashes on ppc64el systems (and perhaps
+  others).
+
+- Added GUID C91818F9-8025-47AF-89D2-F030D7000C2C (0x3900) for Plan 9.
+
+- Added GUID 69DAD710-2CE4-4E3C-B16C-21A1D49ABED3 (0x8307) for 32-bit ARM
+  Linux root (/) partition, as per the Freedesktop.org Discoverable
+  Partition Spec
+  (http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/).
+
+- Edited man pages to clarify that default alignment is to 1MiB boundaries;
+  this translates to 2048 sectors on disks with 512-byte sectors, but it
+  will be something else on disks with other sector sizes.
+
+- Changed behavior of -z/--zap and -Z/--zap-all options to sgdisk so that
+  if a subsequent command causes changes, they'll be written to disk.
+  Previously, doing something like "sgdisk --zap-all --clear /dev/sdd"
+  would wipe the disk but not create a partition table; to create a blank
+  table you'd need to do "sgdisk --zap-all --clear --mbrtogpt /dev/sdd",
+  which is a bit odd and counter-intuitive, to the point of arguably being
+  a bug.
+
+1.0.0 (3/16/2015):
+------------------
+
+- I'm now building a binary package of gdisk_x64.efi, using the UEFI GPT
+  fdisk package.
+
+- Added partition type for OpenBSD data
+  (824CC7A0-36A8-11E3-890A-952519AD3F61/0xa601). Also mapped 0xa600 to the
+  FreeBSD disklabel type code (516E7CB4-6ECF-11D6-8FF8-00022D09712B). I'm
+  not sure that's 100% correct, but since I can't find references to an
+  OpenBSD disklabel GPT type code, it seems the best choice at the moment.
+
+- Added partition type for Windows Storage Spaces
+  (E75CAF8F-F680-4CEE-AFA3-B001E56EFC2D/0x4202)
+
+- Added -O/--print-mbr option to sgdisk, enabling easier display of MBR
+  data structures without invoking gdisk.
+
+- Updated warning message: "EBR describes a logical partition" now reads
+  "EBR points to an EBR," which is more technically correct.
+
+- Altered warning displayed when run from Windows on non-GPT disk, because
+  Windows on UEFI-based systems is becoming more common.
+
+- Fixed spurious "1" return value in gdisk.
+
+- Small code changes to support compilation as EFI application with the
+  UEFI GPT fdisk library
+  (http://sourceforge.net/projects/uefigptfdisk/?source=directory)
+
+- Added new partition type codes for Ceph
+  (https://github.com/ceph/ceph/blob/9bcc42a3e6b08521694b5c0228b2c6ed7b3d312e/src/ceph-disk#L76-L81):
+  4FBD7E29-9D25-41B8-AFD0-062C0CEFF05D/0xf800 (Ceph OSD),
+  4FBD7E29-9D25-41B8-AFD0-5EC00CEFF05D/0xf801 (Ceph dm-crypt OSD),
+  45B0969E-9B03-4F30-B4C6-B4B80CEFF106/0xf802 (Ceph journal),
+  45B0969E-9B03-4F30-B4C6-5EC00CEFF106/0xf803 (Ceph dm-crypt journal),
+  89C57F98-2FE5-4DC0-89C1-F3AD0CEFF2BE/0xf804 (Ceph disk in creation), and
+  89C57F98-2FE5-4DC0-89C1-5EC00CEFF2BE/0xf805 (Ceph dm-crypt disk in
+  creation)
+
+- Added new partition type codes from
+  http://www.freedesktop.org/wiki/Specifications/DiscoverablePartitionsSpec/:
+  44479540-F297-41B2-9AF7-D131D5F0458A/0x8303 (Linux / on x86),
+  4F68BCE3-E8CD-4DB1-96E7-FBCAF984B709/0x8304 (Linux / on x86-64),
+  B921B045-1DF0-41C3-AF44-4C6F280D3FAE/0x8305 (Linux / on 64-bit ARM),
+  3B8F8425-20E0-4F3B-907F-1A25A76F98E8/0x8306 (Linux /srv).
+
+0.8.10 (3/2/2014):
+------------------
+
+- Added feature to sgdisk's -A/--attributes, -c/--change-name,
+  -t/--typecode, and -u/--partition-guid commands: If a -n/--new option
+  with "0" as the partition number precedes these options on the command
+  line, passin "0" as the partition number to the following options causes
+  them to use the newly-created partition. For instance, "sgdisk -n
+  0:0:+550M -t 0:EF00 /dev/sda" creates a new partition with a type code of
+  EF00. (Previous versions would ignore the "-t 0:EF00" option.)
+
+- Fixed bug that caused incorrect partition number to be displayed by
+  sgdisk in error messages when the user specified a non-existent partition
+  for inclusion in a hybrid MBR or conversion to a conventional MBR.
+
+- Fixed new (in 0.8.9) bug that caused a failure to create more than one
+  hybridized partition when creating a hybrid MBR.
+
+- Fixed bug that caused gdisk and sgdisk to create hybridized partitions
+  that ended at or above the 2^32 sector point with incorrect end values.
+  The behavior now varies between gdisk and sgdisk: gdisk now creates
+  hybrid partitions that begin below 2^32 sectors and that are smaller than
+  2^32 sectors, since this is technically legal; but gdisk displays a
+  warning, because some OSes (such as DOS, Windows XP, OS/2, and BeOS)
+  misbehave with such partitions. AFAIK, only Linux, FreeBSD, and Windows 7
+  work properly with such partitions. Because of this fact and because
+  sgdisk is a more automated tool, it's stricter in how it handles things:
+  It refuses to create a hybrid partition if the original ends at or above
+  the 2^32 sector mark.
+
+0.8.9 (2/17/2014):
+------------------
+
+- Removed dependency on libicu for UTF-16 support.
+
+- Fixed spurious "0xEE partition doesn't start on sector 1" warning in
+  FixParts (and perhaps in other programs under some circumstances).
+
+- Added GPT regeneration command to GPT-destruction options ('z' in gdisk,
+  -z and -Z options to sgdisk). This is done to avoid wiping out data
+  mid-disk that might not be backup GPT data structures, which could
+  otherwise occur if a RAID array was resized in certain ways.
+
+- Added check for an oversized 0xEE protective partition. The program now
+  auto-repairs this condition on loading if the GPT data seem otherwise
+  valid. This is done because I've been receiving reports of some disks
+  (possibly from some OEM Windows 8 loads) that violate the GPT spec in
+  this way, and gdisk was reporting write errors when saving data.
+
+- If the GPT data seem to be damaged in some way or if the disk seems to
+  be a hybrid MBR and if the MBR partition(s) don't fit on the disk, the
+  verify (v) function now warns of this condition, and writing the disk if
+  it exists also displays a more specific error message about the problem.
+
+- Added new type codes (3000, 7412F7D5-A156-4B13-81DC-867174929325 and
+  3001, D4E6E2CD-4469-46F3-B5CB-1BFF57AFC149) for Open Network Install
+  Environment (ONIE) boot and config partitions, respectively.
+
+- Added new type ccde (ED01, BFBFAFE7-A34F-448A-9A5B-6213EB736C22), for
+  Lenovo's ESP-like partition.
+
+0.8.8 (10/14/2013):
+-------------------
+
+- Fixed bug that could cause segfault when passing an invalid partition
+  number to sgdisk's -i/--info command.
+
+- Added new type code: 933AC7E1-2EB4-4F13-B844-0E14E2AEF915, or gdisk code
+  8302, for Linux /home partitions. This type code is used by recent
+  versions of systemd to permit /home to be auto-mounted; see
+  http://www.freedesktop.org/software/systemd/man/systemd-gpt-auto-generator.html
+  for details.
+
+- Added new type code: 9E1A2D38-C612-4316-AA26-8B49521E5A8B, or gdisk code
+  4100, for PowerPC PReP (PowerPC reference platform) boot.
+
+- The number of partition type codes has grown large enough that it fills
+  an 80x24 display. I've therefore added a pause (with a prompt to hit
+  <Enter>) to display more items after showing 21 lines in gdisk or after
+  the screen has nearly filled with entries in cgdisk. There's no such
+  pause/prompt in sgdisk, though.
+
+- Fine-tuned verification ('v') check for 0xEE partition that doesn't begin
+  on sector 1: Previously, a disk with multiple 0xEE partitions would
+  always trigger this warning. Now, the warning occurs only if NONE of the
+  0xEE partitions begins on sector 1.
+
+- Fixed hybrid MBR creation on disks larger than 2TiB: Previously, if one
+  opted to create an extra partition to cover unused space following
+  hybridized partitions, gdisk would hang.
+
+- Added check for an active/bootable 0xEE protective partition to the
+  verify ('v') function. If found, this is not counted as an error, but
+  it is called out to the user, since it can cause some EFIs (such as
+  VirtualBox's EFI) to ignore the disk.
+
+0.8.7 (7/8/2013):
+-----------------
+
+- Modified Mac version so that it can work on /dev/rdisk* devices as well
+  as /dev/disk* devices. The result is that, when using the /dev/rdisk*
+  devices, the partition table can sometimes be re-read without removing
+  the disk or rebooting.
+
+- Added "-a" option to cgdisk to use a ">" symbol to the left of the
+  selected partition rather than ncurses highlighting.
+
+- Modified "converting MBR to GPT" message to clarify that the conversion
+  is being held in memory, since some people have mistakenly assumed that a
+  "gdisk -l" operation will change an MBR disk to a GPT disk without
+  prompting.
+
+- Added partition type code for freedesktop.org's proposed $BOOT partition
+  (bc13c2ff-59e6-4262-a352-b275fd6f7172; GPT fdisk type code EA00)
+
+- Adjusted alignment code when using -n or -N in sgdisk to keep the
+  requested partition size (if specified using +###{MGT} terminology)
+  as the requested value rather than relative to the requested start
+  point. This gives you the requested partition size rather than be
+  slightly smaller if sgdisk needs to adjust the start point up a bit and
+  it prevents gaps from appearing between partitions if several are created
+  in succession using automatic placement of the start point.
+
+- Fixed small bugs in gdisk_test.sh script.
+
+- Removed stray debug message that would appear when reading MBR disks.
+
+- Added partition type code for Intel Rapid Start partition (GUID
+  D3BFE2DE-3DAF-11DF-BA40-E3A556D89593, code 8400), used by systems that
+  implement Intel's Rapid Start technology. See
+  http://blog.adios.tw/2012/10/funtoo-linux-and-intel-rapid-start.html or
+  http://mjg59.dreamwidth.org/26022.html.
+
+- Added partition type code for Haiku BFS (GUID
+  42465331-3BA3-10F1-802A-4861696B7521; code EB00).
+
+0.8.6 (1/9/2013):
+-----------------
+
+- Fixed a bug that could cause sgdisk to crash when passing a partition
+  number of 0 to the -t option.
+
+- Added support for building under Solaris.
+
+- Added a new check to the verification code.
+
+- Added partition type code for Sony system partition
+  (F4019732-066E-4E12-8273-346C5641494F). I'm not entirely clear what this
+  is used for, but it's appearing on some new Sony computers.
+
+- Tweaked hybrid MBR creation options to fix a problem that caused the main
+  0xEE MBR partition to NOT be created if the user told gdisk to NOT place
+  it at the start of the disk AND IF fewer than three partitions are
+  hybridize AND IF the user opted to create a second protective partition.
+
+- Changed default build options for Mac OS X to *NOT* use libicu,
+  since it seems to have broken somewhere along the line. It still
+  works on Linux, though.
+
+- Added partition type codes for VMWare ESX (FB00, FB01, and FC00).
+
+0.8.5 (5/30/2012):
+------------------
+
+- Changed code that writes the partition table so that a disk sync
+  operation occurs even if one or more write operations failed (but not if
+  they all failed). This is intended to work around a bug that a user
+  reported on a Windows system on which the write of the protective MBR
+  failed, although everything else worked. (I suspect anti-virus software
+  may have been blocking write access to the MBR.)
+
+- Added type codes for Midnight BSD (0xA580 - 0xA585). I used these codes
+  because Midnight BSD uses the same 0xA5 type code as FreeBSD on MBR
+  disks, so I'm starting Midnight BSD's numbering halfway through the
+  0xA5## range.
+
+0.8.4 (3/25/2012):
+------------------
+
+- REALLY fixed Ctrl+D problems! Now gdisk terminates upon receiving a
+  Ctrl+D. In all previous versions, it could lock itself into a CPU-hogging
+  loop if launched via "sudo" from a terminal window that was then closed
+  or if Ctrl+D was pressed at certain input prompts (for a partition name
+  or sector number, for instance).
+
+0.8.3 (3/23/2012):
+------------------
+
+- Fixed compilation problem on GCC 4.7.
+
+- Improved handling of Ctrl+D on some systems.
+
+- Added disk's name to message stating that a disk write was successful.
+
+- Fixed bug that caused creation of >2TiB partitions on 32-bit systems to
+  be truncated in sgdisk.
+
+0.8.2 (1/22/2012):
+------------------
+
+- Adjusted the code to support a number of partitions that's not a multiple
+  of the number of partition table entries that fits in a sector (normally
+  4 per sector). The program still rounds up, when necessary, when resizing
+  the partition table manually, but not when loading a partition table that
+  contains a peculiar number of partitions. This helps prevent spurious
+  error messages about CRC problems when loading some Solaris partition
+  tables.
+
+- Fixed bugs relating to the handling of empty partitions; Solaris's ZFS
+  tools create weird empty partitions that are legal but that gdisk wasn't
+  handling properly. (Specifically, they sometimes have non-zero end
+  points; gdisk assumed empty partitions had end points of 0.)
+
+- Fixed a bug that caused an infinite loop of input prompts if the user
+  pressed Ctrl+D.
+
+- Changed gdisk's first-sector input operation to specify a sector number
+  that's properly aligned as the default value. This eliminates the need
+  to alter that value and notify the user of the change when the user
+  hits "Enter" for the default value as the first partition on an empty
+  disk (as well as in some other situations).
+
+0.8.1 (10/1/2011):
+------------------
+
+- Fixed bug that could cause FixParts to keep a partition's assignment
+  as logical when FixPart could not actually do so. This could happen
+  when there are no gaps between two logical partitions. Some partitioning
+  tools can create such configurations, but FixParts can't. Such
+  configurations are extremely rare. I've only encountered them when
+  logical partitions are out of order.
+
+- Added code to detect infinite loops of logical partitions when reading
+  MBR data. When detected, the program now stops reading after the first
+  loop, so no duplicates appear in the partition list.
+
+- Fixed bug in partition overlap detection in MBR code.
+
+- Changed GPT reading code to use the size encoded in GPT headers to
+  determine how much of the header to use in computing a CRC, with the
+  restriction that the size be equal to or less than the disk's sector
+  size. This should work around problems with libefi in ZFS, which sets the
+  header size to 512 rather than the more common 92. A caveat: If the
+  disk's sector size is larger than the GPTHeader data structure size (512
+  bytes), then the rest of the sector's contents are ignored and replaced
+  with 0 values. This could produce false positives on CRC checks on disks
+  with over-512-byte sector sizes if the header sector is padded with
+  something other than 0 values.
+
+- Fixed bug in new (as of 0.8.0) check that main and backup partition
+  tables are identical on big-endian (PowerPC, etc.) hardware.
+
+0.8.0 (9/10/2011):
+------------------
+
+- Added new return option for sgdisk: 8, which means that a replication
+  operation (-R or --replicate) failed. Note that other operations on
+  the same command line might still have succeeded.
+
+- Added gdisk_test.sh shell script, contributed by Guillaume Delacour.
+  This script tests some common gdisk and sgdisk operations to be sure
+  they're working correctly.
+
+- Enable sgdisk's -l (--load-backup) and -o (--clear) options to work
+  even on disks that are damaged. Most other options will still be ignored,
+  though, so if you suspect a disk may be bad and want to use one of these
+  options, you should do so on a line by itself, followed by a separate
+  command to perform other actions (such as adding new partitions).
+
+- Added check for mis-matched primary and backup partition tables.
+  A mismatch is reported as a CRC error.
+
+- Added Apple Core Storage partition type code (hex code AF05, GUID
+  53746F72-6167-11AA-AA11-00306543ECAC).
+
+- Added cgdisk program to the family. This program is a rough workalike
+  to cfdisk, much as gdisk is a rough workalike to fdisk. See the cgdisk
+  man page or http://www.rodsbooks.com/gdisk/cgdisk-walkthrough.html for
+  details about its operation.
+
+- Fixed bug that caused CHS end point for protective MBR to be set to
+  0xfeffff rather than the spec-mandated 0xffffff on disks over ~8GB. This
+  is a very minor bug, since not much cares about this, and most other GPT
+  tools get it wrong in the same way, too.
+
+0.7.2 (6/26/2011):
+------------------
+
+- The Windows version now (finally!) generates proper GUIDs rather than a
+  purely random number. This fixes a bug that caused Windows 7 to crash
+  when converting a disk from MBR format (but, oddly, not when creating a
+  fresh partition table or doing various other things).
+
+- Added a warning when an MBR partition is discarded because it's too
+  big for the disk.
+
+- Changed warning to Windows users about the dangers of converting to GPT
+  so that it appears only on disks that aren't already in GPT form.
+
+- Fixed bug that caused bogus "3" values to pad the ends of partition names
+  on some disks (particularly those created by Microsoft's disk
+  partitioning tools).
+
+- Made compilation without Unicode support possible (see README file)
+
+- Made default filesystem type code OS-dependent (based on the compilation
+  platform).
+
+- Added new Linux-only filesystem partition type GUID code,
+  0FC63DAF-8483-4772-8E79-3D69D8477DE4 (8300 entry code). Also changed name
+  of the EBD0A0A2-B9E5-4433-87C0-68B6B72699C7 (0700 entry code) to
+  "Microsoft basic data").
+
+- Fixed a bug that caused an incorrect code to be set for active/bootable
+  partitions when generating a hybrid MBR.
+
+- Enable entry of hex codes that begin with "0x" for both GPT and MBR
+  partitions.
+
+- Fixed bug that caused the boot loader code to be lost when creating a
+  hybrid MBR.
+
+- Fixed bug in sector input code that could produce improper values
+  if the user inputs ridiculously large "+" values.
+
+0.7.1 (3/21/2011):
+------------------
+
+- Added support for proper UTF-16LE partition names rather than the
+  "shortcut" that properly encoded only ASCII names. This support works
+  only in Linux, FreeBSD, and OS X, though, at least for the moment.
+  Although it's possible to compile this support into Windows when using
+  Visual C++, it doesn't seem to work properly. Since using this feature
+  would require distributing the ICU libraries with the Windows binary,
+  thus bloating the binary package's size to no effect, I've disabled it in
+  my standard Windows build, at least for now.
+
+- Added check to fixparts to keep it from operating on devices that
+  lack an existing MBR signature. (In 0.7.0, it could write an empty
+  MBR data structure to a device on which it was mistakenly launched.)
+
+- Fixed bug that caused the protective MBR to not be written when
+  restoring a backup of the GPT data.
+
+- Fixed bug that caused second protective MBR partition, when created
+  as part of a hybrid MBR, to always be of type 0xEE, even when the
+  user specified something else.
+
+- Integrated a number of code cleanups contributed by Florian Zumbiehl.
+
+0.7.0 (3/11/2011):
+------------------
+
+- Fixed bug that caused some types of logical partitions to be misread.
+
+- Created FixParts program, to fix problems on MBR-partitioned disks.
+  Although this program is part of the GPT fdisk family, it is NOT used on
+  GPT disks.
+
+- Completely redid the GPT-to-MBR code, used both for converting to MBR
+  form and for creating hybrid MBRs.
+
+- Fixed a bug that caused gdisk to "forget" some partitions if there were
+  numbering gaps when a conversion to MBR was aborted.
+
+- Improved CHS value creation on small (<~8GB) disks for protective MBR
+  and when creating hybrid MBRs or converting to MBR format. Linux-only,
+  for the moment; other platforms still produce bad CHS values on sub-~8GB
+  disks (but few OSes care these days).
+
+- Enhanced disk replication features ('u' on the experts' menu in gdisk; -R
+  or --replicate in sgdisk). It's now possible to replicate the partition
+  table from a larger to a smaller disk, so long as all the partitions fit
+  on the smaller disk. In sgdisk, the secondary GPT data are moved
+  automatically if disk sizes don't match. In gdisk, the secondary GPT data
+  are moved automatically if the target disk is smaller than the source
+  disk; if the target disk is larger than the source disk, the user is
+  given the option of making this adjustment.
+
+- Fixed --load-backup (-l) option to sgdisk, which was broken.
+
+- Changed largest drive that's not given a minimum 4 KiB alignment even
+  when smaller alignment is detected on the disk to 300 GB.
+
+- Fixed bug that prevented aborting a partition table backup ('u' on the
+  experts' menu) by hitting the Enter key for the device filename.
+
+- Implemented a number of code cleanups provided by Florian Zumbiehl.
+
+0.6.14 (1/8/2011):
+------------------
+
+- Made small change to the way the start sector is interpreted if you use a
+  "+" specification, as in "+2G" to locate a partition 2 GiB into the
+  default range. This change makes adjustments for sector alignment less
+  likely.
+
+- Modified sgdisk's -n (--new) option to work with relative start and end
+  values (which the man page incorrectly stated it already did). Values of
+  0 for the start and end sectors refer to the first and last available
+  sectors in the largest free block, and a partition number of 0 refers to
+  the first available partition.
+
+- Added ChromeOS GUID values to list of recognized partition type GUIDs.
+  7F00 = ChromeOS kernel, 7501 = ChromeOS root, 7502 = ChromeOS reserved.
+  Untested on actual ChromeOS system.
+
+- Tweaked APM detection to look for APM signature even if an MBR
+  signature has already been found. Helps in diagnosis of cases
+  in which an MBR has overwritten an APM disk.
+
+0.6.13 (10/12/2010):
+--------------------
+
+- Added notification about nonexistent partitions to hybrid MBR creation
+  in gdisk.
+
+- Fixed bug in GPT-to-MBR conversion that could sometimes enable creation
+  of an extended partition that overlaps a preceding partition.
+
+- Fixed bug in GPT-to-MBR conversion that prevented creation of an MBR
+  table with logical partitions if there were four or fewer partitions.
+
+0.6.12 (10/7/2010):
+-------------------
+
+- Adjusted alignment code to use 1 MiB alignment by default for drives with
+  other than 512-byte sector sizes. (Previous versions increased this --
+  for instance, to 4 MiB for drives with 2048-byte logical sector size.)
+
+- Entry of non-hexadecimal value for partition type code now causes
+  re-prompting for a new value, fixing a recently-introduced minor bug.
+
+- Fixed bug in sector entry using K/M/G/T/P suffixes on disks with
+  other-than-512-byte sector numbers.
+
+- Added "P" (PiB, pebibyte) suffix to suffixes accepted in entering
+  partition sizes.
+
+- Fixed bug that caused sgdisk to segfault if fed the (invalid)
+  "-A show" parameter. Now it terminates with a complaint about an invalid
+  partition number 0.
+
+- Removed warning when running on big-endian hardware, since this
+  support has been present for quite a while with no bug reports.
+
+0.6.11 (9/25/2010):
+-------------------
+
+- Added -F (--first-aligned-in-largest) option to sgdisk. This option is a
+  variant on -f (--first-in-largest); it returns the number of the first
+  sector that will be used in the largest free area, given the current
+  alignment value (set via -a/--set-alignment).
+
+- Streamlined GUID code entry in gdisk; it no longer offers the option
+  to enter GUIDs in separate segments.
+
+- The -t option to sgdisk now accepts GUID values as well as the
+  sgdisk/gdisk-specific two-byte hex codes.
+
+- Added check that the protective 0xEE MBR partition begins on sector 1
+  to the verify function. If it doesn't, a warning message is displayed,
+  but it doesn't count as an error.
+
+- Added check for overlapping MBR partitions to verify function (gdisk "v"
+  function on all menus; sgdisk -v/--verify function). Also warns about
+  multiple MBR 0xEE partitions (causes problems in some OSes).
+
+- Added check to GPT-to-MBR and hybrid MBR creation options to prevent
+  creation of disks with duplicate partitions. When told to create a disk
+  with duplicates, sgdisk now aborts with the error message "Problem
+  creating MBR!" When attempting to create a hybrid MBR with duplicates,
+  gdisk silently drops duplicate partitions, leaving fewer than requested.
+  Creating duplicates should not be possible in sgdisk when converting to
+  MBR form.
+
+0.6.10 (8/22/2010):
+-------------------
+
+- Enable disk-wipe (-z and -Z) and verification (-v) operations in
+  sgdisk even if the disk is badly damaged.
+
+- Added support for setting attributes in sgdisk (-A/--attributes option)
+  in sgdisk.
+
+- Fixed bug that created backwards attribute field values (bit #2 was
+  entered as bit #61, etc.).
+
+- Fixed bug that caused creation of hybrid MBR to wipe out the MBR's boot
+  code.
+
+- Added ability to save partition table from one device to another (gdisk:
+  'u' on experts' menu; sgdisk: -R or --replicate option).
+
+- Fixed inaccessible -C/--recompute-chs option in sgdisk.
+
+0.6.9 (7/4/2010):
+------------------
+
+- Fixed minor error in sgdisk man page (--largest-new option requires
+  a partition number).
+
+- Fixed major bug in hybrid MBR creation, which caused incorrect
+  protective partition end point settings and occasionally other
+  problems.
+
+0.6.8 (5/23/2010):
+------------------
+
+- Added tests to see if the file to be opened is a directory, character
+  device, FIFO, or socket; program now terminates if any of these
+  conditions is met. (Linux/FreeBSD/OS X only.) Thanks to  Justin Maggard
+  for this patch.
+
+- Added 'f' option on gdisk's experts' menu (-G/--randomize-guids in
+  sgdisk). This option randomizes the disk's GUID and all partitions'
+  GUIDs. Intended for use after cloning a disk with a utility that copies
+  the GUIDs intact (such as a raw dd copy) if you want each disk copy to
+  have its own set of GUIDs.
+
+- Added -u/--partition-guid and -U/--disk-guid options to sgdisk. These are
+  the equivalents of the 'g' and 'c' options, respectively, on the gdisk
+  experts' menu: They enable adjusting an individual partition's GUID or a
+  disk's GUID. The GUID may be either a fully specified GUID value or 'R'
+  or 'r' to set a random GUID value.
+
+- Fixed compile problem for FreeBSD (its math library lacks a log2()
+  function). Also created separate Makefile.freebsd with a couple of
+  FreeBSD-specific options.
+
+- Added -N (--largest-new) command to sgdisk. This command creates a single
+  partition that fills the largest single unpartitioned block of space on
+  the disk.
+
+- Fixed sgdisk man page error: the --change-name option was incorrectly
+  listed as --change.
+
+- Added 'h' option to gdisk experts' menu (-C or --recompute-chs in sgdisk)
+  to recompute all protective/hybrid MBR CHS values. This option is
+  intended to work around a bug in at least one BIOS that prevents the
+  computer from booting when the GPT-mandated (but technically illegal)
+  0xFFFFFF CHS value is used as the end point for a protective MBR. The
+  recomputed values will be legal (e.g., 0xFEFFFF instead of 0xFFFFFF),
+  but incorrect in GPT terms, and will therefore enable at least one
+  BIOS to boot with a GPT disk. See http://www.rodsbooks.com/gdisk/bios.html
+  for all I know about BIOS/GPT incompatibilities.
+
+0.6.7 (5/1/2010):
+-----------------
+
+- Undid earlier change, with version 0.6.4, that wiped the MBR boot loader
+  when doing MBR-to-GPT conversions. I've now become skeptical that MBR
+  boot loaders were causing any real problems on GPT disks, so I'm going
+  back to the philosophy of leaving as much alone as possible.
+
+- Fixed bug that caused incorrect reporting of free space on 0-size disks
+  (e.g., files of 0 length passed as disk images).
+
+- Fixed bug that caused segfault on some invalid disks
+
+- Fixed bug that caused incorrect partition numbers to be displayed for
+  some verify problems.
+
+
+0.6.6 (3/21/2010):
+-----------------
+
+- Added support for the "no block IO protocol" (referred to as "hide from
+  EFI" in GPT fdisk) and "legacy BIOS bootable" attribute bits. See Table
+  19 of the UEFI 2.3 specification (p. 153) for details.
+
+- Changed the sequence in which GPT data structures are written to disk;
+  backups are now written first, followed by the main structures. This is
+  as recommended in the UEFI 2.3 specification, since it's safer in the
+  extremely unlikely event that a RAID array's size is increased and
+  there's a power outage mid-write. (If the main structures are written
+  first in this case, they'll point to data that's not yet been written;
+  but by writing the backups first, the old main structures will still
+  point to the valid old backup structures.)
+
+- Protective MBRs now have disk signatures of 0x00000000, to better
+  conform with GPT as described in the UEFI 2.3 specification.
+
+- Added alignment information to the summary data produced by the
+  'p' main-menu option in gdisk or the -p option to sgdisk.
+
+- More alignment changes: GPT fdisk now attempts to determine the alignment
+  value based on alignment of current partitions, if any are defined. If no
+  partitions are defined, a default value of 2048 is set. If the computed
+  value is less than 8 on drives over about 596GiB, it's reset to 8, since
+  the drive might be a WD Advanced Format unit that requires an 8-sector
+  (or larger power-of-2) alignment value for best performance. The
+  2048-sector default provides better alignment in some RAID
+  configurations.
+
+- Changed behavior when a backup restore fails. Previously, GPT fdisk
+  would create a fresh blank set of partitions. Now it does so only
+  if the failure occurs when interpreting the backup's contents; if the
+  user typed the wrong filename, the in-memory data structures aren't
+  touched.
+
+
+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
+left the version number in the program at 0.6.4.
+
+- Altered Makefile to pass user's compiler and linker environment
+  variables through.
+
+- Added #include to gpttext.cc to enable it to compile on the latest
+  GCC versions (it was failing on at least some 4.4.x compilers).
+
+0.6.4 (2/19/2010):
+-------------------
+
+- Added -m (--gpttombr) option to sgdisk, enabling conversion of GPT
+  disks to MBR format, with a limit of four partitions total, and of course
+  without overcoming the 2TiB limit.
+
+- Added -h (--hybrid) option to sgdisk, enabling creation of hybrid
+  MBRs. Fewer options are available in sgdisk than in gdisk, though,
+  in order to keep the user interface manageable.
+
+- Fixed off-by-one bug in specification of partition when using the
+  -T (--transform-bsd) option in sgdisk.
+
+- Changed the code to create a new MBR unique disk signature whenever a new
+  protective MBR is generated (when doing an MBR-to-GPT conversion, when
+  using the 'n' option on the experts' menu, or when using the 'o' option
+  on the main menu, for example). Previous versions attempted to preserve
+  the existing MBR disk signature in most cases, but this resulted in
+  values of 0x00000000 whenever an empty disk was partitioned, and often in
+  other cases, too. Better to risk changing this value too often than to
+  leave multiple disks with 0x00000000 values, I think.
+
+- Added transpose ('t' on experts' menu in gdisk; or -r or --transpose in
+  sgdisk) command to enable fine-tuning partition order without doing a
+  full sort.
+
+- Added code to clear the MBR boot loader when doing an MBR-to-GPT
+  conversion. (This was already done in full-disk BSD-to-GPT conversions.)
+  This is done because I've seen a few problem reports that make me think
+  some MBR boot loaders freak out and hang the system when they encounter
+  GPT disks, and/or they attempt to load a second-stage boot loader stored
+  in what is now GPT territory, causing a system hang. Since MBR boot
+  loaders don't work on GPT disks anyhow (even GRUB needs to be
+  reinstalled), this new wiping behavior shouldn't cause any problems, and
+  may prevent a few.
+
+- Fixed bug in Windows version that prevented saving backup files.
+
+- Fixed bug that caused second and subsequent partition numbers in
+  prompts in hybrid MBR conversion procedure to be displayed in
+  hexadecimal.
+
+- Fixed very obscure potential bug in hybrid MBR/GPT synchronization when
+  deleting partitions; code wasn't matching partition lengths correctly,
+  which would only affect partitions that start at the same point but have
+  different lengths in MBR vs. GPT.
+
+- Fixed bug in the -E option to sgdisk; it was actually returning the
+  last free sector, not the last free sector in the largest free block.
+
+- Fixed bug in -t option to sgdisk; it was corrupting partition type
+  codes.
+
+- Fixed minor alignment bug in partition summary list ('p' from any menu)
+  when partition sizes are between 1000 and 1024 units.
+
+- Backup restore function ('l' on recovery & transformation menu) now
+  accepts both backups generated by GPT fdisk and backups created by a
+  direct copy (via dd, etc.) of the MBR, main GPT header, and main GPT
+  partition table, in that order. ("dd if=/dev/sda of=backup.gpt bs=512
+  count=34" will do this on Linux for a disk with a typical-sized GPT table
+  of 128 entries.)
+
+0.6.3 (2/3/2010):
+------------------
+
+- Fixed serious data corruption bug on big-endian (PowerPC and similar)
+  systems.
+
+- Changed several GPT fdisk Solaris type codes to correct a duplicate
+
+- Corrected error in GPT fdisk type codes for NetBSD LFS and NetBSD RAID;
+  they were identical, but I've now changed NetBSD RAID to A906, which
+  is unique.
+
+- Added GUID for IBM General Parallel File System (GPFS) partition type
+  code. Somewhat arbitrarily set it to use the 7501 number (MBR code 0x75
+  is used by IBM PC/IX, so it's at least the right company, by my loose
+  numbering rules....).
+
+- Improved GUID generation. Prior versions generated completely random
+  numbers for GUIDs. This works, but is technically a violation of the
+  spec. Unix versions now employ libuuid to generate GUIDs in a more
+  correct way. The Windows version still generates random numbers, though.
+
+- Turned PartTypes class into a derived class of GUIDData, and renamed
+  it to PartType.
+
+- Created new GUIDData class, to replace the original GUIDData struct.
+
+0.6.2 (1/29/2010):
+------------------
+
+- The change-type ('t' on main menu) option now changes the partition's
+  name *IF* the current name is the generic one for the partition type.
+  If the current name is not the generic name, it is NOT changed.
+
+- Fixed bug that caused new protective MBR to not be created when the
+  MBR was invalid and the GPT was damaged and the user opts to try to
+  use the GPT data.
+
+- Enabled default partition type code of 0700 when creating partitions
+  or changing their type codes. (Type 0700, Linux/Windows data, is set if
+  the user hits the Enter key alone.)
+
+- Fixed bug in sort ('s' on main menu) option that caused partition
+  numbers to begin at more than 1 if the original partition list had
+  too many empty partitions before the last one defined.
+
+- Improved code to determine which partition table to load in case of
+  CRC mismatches between the partition tables and the stored CRC values
+  in the headers.
+
+- Compiles using MinGW (http://www.mingw.org) to create a Windows binary.
+
+- Moved all disk I/O functions to the new DiskIO class. This helps with the
+  Windows port; it uses diskio-windows.cc for Windows-specific code,
+  diskio-unix.cc for the Linux, FreeBSD, and OS X code, and diskio.cc for
+  cross-platform disk I/O code.
+
+- Changed BSD disklabel detection code to be more correct (I think).
+  This change has no effect on my test disks, but I hope it'll work
+  better on disks with sector sizes other than 512 or 2048.
+
+0.6.1 (1/20/2010):
+------------------
+
+- Fixed bug that returned incorrect disk size on 32-bit versions of
+  FreeBSD.
+
+- Fixed bug that prevented FreeBSD version from working on disk image
+  files.
+
+- Fixed bug that caused BSD disklabel conversion to fail.
+
+0.6.0 (1/15/2010):
+------------------
+
+- Fixed bug that caused the convert to MBR function to fail.
+
+- Added support for disks with other than 512-byte sectors.
+
+- Created embryonic sgdisk program.
+
+- Fixed bug that caused relative sector numbers entered by users (e.g,
+  "+128M") to be misinterpreted as from the start of the range rather than
+  from the default value.
+
+0.5.3 (1/4/2010):
+-----------------
+
+- Fixed bug in display of GUIDs when compiled with some versions of GCC.
+
+- Eliminated warnings caused by additional checks in latest versions of
+  GCC. These warnings were harmless, but to eliminate them I've added
+  more error checking on disk I/O.
+
+- Eliminated unnecessary warnings about potential data loss if the program
+  was launched with the -l option or if writes aren't possible.
+
+- Added code to set the partition boundary value based on the physical
+  sector size. (FindAlignment() function.) This function, however, works
+  only on Linux, and then only if the BLKPBSZGET ioctl is defined. This
+  ioctl is new in kernel 2.6.32 or thereabouts.
+
+0.5.2 (12/31/2009):
+-------------------
+
+- Modified partition creation function to begin partitions on 8-sector
+  boundaries by default. This improves performance on the new Western
+  Digital Advanced Format drives. The new 'd' and 'l' options on the
+  experts' menu display and change, respectively, the boundary size.
+
+- Tweaked code to produce fewer warnings on the latest versions of
+  GCC.
+
+0.5.1:
+------
+
+- Made some minor edits to the man page.
+
+- Incorporated RPM .spec file changes contributed by Scott Collier
+  (boodle11@gmail.com).
+
+- Changed method of locating and loading backup GPT data, to use the
+  main header's pointer, if it's valid, rather than seeking to the
+  end of the disk.
+
+- Added 'e' option (relocate backup GPT data structures) to the experts'
+  menu.
+
+- Fixed bug that prevented recovery of partitions in case of partially
+  damaged GPT data (bad main and good backup or bad backup and good
+  main header, for instance).
+
+0.5.0:
+------
+
+- Added GPT-to-MBR conversion function. It's very limited, but potentially
+  useful in some cases.
+
+- Fixed bug that caused incorrect file sizes to be reported on 32-bit
+  Linux, thus causing problems when editing partition tables in disk images
+  or when loading GPT backup files.
+
+- Fixed bug that caused bogus CRC error reports when loading backup GPT
+  data.
+
+- Reorganized menus. There are now three: the main menu, the experts' menu,
+  and the recovery & transformation menu. The last of these has most of the
+  items that had been on the earlier versions' experts' menu.
+
+- Added ability to re-load the MBR and generate a fresh GPT from it. This
+  is normally identical to quitting and re-running the program, but it
+  could be handy if, say, the GPT partitions on a hybrid configuration are
+  badly messed up; this will enable using the hybridized partitions as the
+  starting point for a new GPT setup.
+
+- The program now generates CHS values for hybrid and GPT-to-MBR conversion
+  MBRs. For the moment, the assumption is the maximum number of heads and
+  sectors per track (255 and 63, respectively), although the bulk of the
+  code supports other values -- it'd just be awkward to enter the data in
+  the user interface.
+
+- Fixed minor display bug that caused number of sectors on the disk to be
+  shown as 0 on large disks when running 32-bit binaries.
+
+- Reverted 0.4.2's zap (destroy GPT) changes, since I don't want to wipe
+  out a valid MBR if the user created that MBR over an older GPT without
+  first properly wiping out the GPT, and the user now wants to wipe out
+  the GPT.
+
+- Reformatted and edited the man page. Aside from edits related to the
+  preceding program changes, I've altered the markup slightly and trimmed
+  much of the more tutorial information from the man page to better
+  conform to typical terse man page style.
+
+0.4.2:
+------
+
+- Code cleanup.
+
+- Fixed very small formatting bug in display of hex code when a match isn't
+  found when converting from an MBR/gdisk hex code to a GUID type code.
+
+- Added the ability to work on disk image files (raw files for virtual
+  machines, backup images, etc.). The program assumes that all such disk
+  image files have 512-byte sectors.
+
+- Added verification prompt to 'o' main-menu option to avoid accidental
+  erasures of all partitions.
+
+- The "destroy GPT data structures" option ('z' on the experts' menu) now
+  also destroys all EFI GPT (0xEE) partitions in the MBR.
+
+- Added an extra warning to the "destroy GPT data structures" option if an APM
+  or BSD disklabel was detected on the disk.
+
+- Added a buffer flush after destroying GPT data structures, to get the OS
+  to read the new (empty or MBR-only) partition table.
+
+- Fixed bug that allowed entry of nonexistent partition numbers when creating
+  a hybrid MBR.
+
+0.4.1:
+------
+
+- Code cleanup/re-organization
+
+- Partition creation function ('n' on main menu) now uses the start of the
+  largest available chunk of free space rather than the first available
+  sector as the default starting sector number. This should enable easier
+  partition creation if there are small bits of free space on the disk.
+
+- You can now specify the end point of a partition by using a minus sign,
+  in which case the end point is the default value minus the specified
+  size. For instance, "-200M" creates a partition that ends 200MiB before
+  the default end point.
+
+- You can now specify the start point of a partition by using a plus or
+  minus sign, in which case the start point is the specified distance from
+  the start (+) or end (-) of free space. This is exactly the same as the
+  new rules for entry of the end point, except that the default value is
+  set differently.
+
+- Deleting a partition now checks for a matching hybrid MBR partition, and
+  if one is found, it's deleted. Any empty space that then surrounds the
+  0xEE (EFI GPT) MBR partitions is then added to the nearby 0xEE partition.
+  If no non-0xEE partitions are left, a fresh protective MBR is generated.
+
+- Added hybrid MBR consistency check to the verify ('v') option and to
+  pre-write checks. If non-0xEE/non-0x00 MBR partitions without
+  corresponding GPT partitions are found, the user is warned. This finding
+  does NOT prevent writing the partition table, though.
+
+- Added non-destructive write test when opening the device file, in order
+  to detect the problem with FreeBSD being unable to write to disks with
+  mounted partitions (or other potential problems).
+
+0.4.0:
+------
+
+- Added support for BSD disklabels. The program can now convert disks that
+  use "raw" disklabels, with the caveat that the first partition will
+  almost certainly need to be deleted because it'll overlap the main GPT
+  header; and convert disklabels contained within a GPT (or a former MBR,
+  converted to GPT) partition. In the latter case, the 'b' main menu option
+  is used.
+
+- Added support for compiling on FreeBSD.
+
+- Fixed bug that could cause crashes or incomplete sorts when sorting
+  the partition table.
+
+- New partitions, including converted ones, now take on the name of the
+  partition type as a default name.
+
+- Reorganized some code; created a separate C++ class for GPT partitions
+  (GPTPart), which replaced a struct and enabled moving code from the
+  bloated GPTData class into GPTPart.
+
+- Fixed a bug that produced spurious warnings about unknown sector sizes
+  when loading a backup file.
+
+0.3.5:
+------
+
+Note: This version was not officially publicly released; I wanted to test
+the big-endian support while developing 0.4.0.
+
+- Tweaked the disk type identification code to warn users to re-sync their
+  hybrid MBRs when one is detected.
+
+- Tweaked MBR-reading code to ignore 0xEE (EFI GPT) partitions. This will
+  only have an effect on a poorly partitioned MBR disk that contains an
+  inappropriate EFI GPT partition, or when attempting to recover a
+  corrupted disk by using the hybrid MBR for data recovery.
+
+- Added big-endian (PowerPC, etc.) support!
+
+- Added code to identify and warn of the presence of an Apple Partition
+  Map (APM) on the disk.
+
+- Enabled MBR conversion code to handle multiple logical partitions.
+
+0.3.4:
+------
+
+- Fixed bug that enabled (possibly accidental) entry of MBR type codes of
+  0x00 in GPTData::MakeHybrid(). The fix also enables entry of default
+  type code by pressing the Enter key when prompted. Applied a similar
+  fix to the entry of the type code for the second protective partition,
+  if one is used.
+
+- Fixed a typo: "sectors" was spelled "sectprs" in one spot!
+
+- Fixed bug that caused default entry for end sector to be refused if an
+  initial value using a plus sign (e.g., "+20G") was also refused.
+
+0.3.3:
+------
+
+- Gave users control over the way MBR partitions are assigned to slots in a
+  hybrid MBR setup; the original method (putting the 0xEE partition after
+  the real partitions) works well for non-boot disks, but both GRUB and
+  GRUB2 become confused by this type of setup, so it needs changing.
+
+- Changed "blocks" to "sectors" in GPT and MBR table displays.
+
+- Added "Boot" column to MBR table display; shows an asterisk (*) when the
+  partition's status is bootable.
+
+0.3.2:
+------
+
+- Changed __DARWIN_UNIX03 to __APPLE__ as code to enable MacOS X support.
+
+- Added the ability to create a hybrid MBR ('h' on experts' menu). This was
+  motivated by my discovery that Windows 7 remains brain-dead when it comes
+  to the ability to boot from a GPT disk, at least on BIOS-based machines.
+
+- Added 'z' option to experts' menu, to destroy GPT data structures and
+  exit. The intent is to use this feature to enable subsequent partitioning
+  of the disk using fdisk or other GPT-unaware tools. (GNU Parted will wipe
+  the GPT data structures itself when you create a new MBR ["msdos
+  disklabel," in Parted parlance], so using Parted is another option.)
+
+- Slightly altered the effect of the 'o' command on the main menu. It now
+  blanks out the protective MBR, as well as the GPT data.
+
+0.3.1:
+------
+
+- Added Mac OS X support, provided as a patch by David Hubbard
+  (david.c.hubbard@gmail.com).
+
+- Fixed bug in disksize() function on Mac OS. (Possibly dependent on the
+  kernel and/or GCC version.) The disk size, of type uint64_t, was not
+  being passed correctly, so I reorganized the function to return it as
+  the function's return value rather than as a parameter. This seems to
+  work OK on my Mac OS test system and on both 32- and 64-bit Linux
+  systems.
+
+- Fixed off-by-one bug in GPTData::FindLastAvailable().
+
+- Fixed bug that caused display of options after a disk-write error.
+
+- Fixed several incorrect MacOS X partition type GUIDs, thanks to Yves
+  Blusseau (1otnwmz02@sneakemail.com).
+
+0.3.0:
+------
+
+- Changed version number to 0.3.0, reflecting the fact that I've received
+  no significant bug reports and so am elevating the program to "beta"
+  status. This change also entailed altering the warning the program
+  displays when saving partition table changes.
+
+- Fixed minor bug in CHS geometry of the protective MBR's type EE partition
+  (was producing 0x000200 as the start value, but should be 0x000100).
+  Should be a non-critical bug since the protective MBR partition
+  definition is only there to keep MBR-only disk utilities from messing
+  with the disk.
+
+- Added ability to enter GUIDs as single massive strings rather than in
+  chunks.
+
+0.2.2:
+------
+
+- Added #include directives required to compile the program using GCC
+  4.4.0.
+
+0.2.1:
+------
+
+- Fixed partition numbering problem in reports of partition overlaps in
+  verification function.
+
+- Fixed 1-sector partition size problem when creating new partitions
+  (partitions were 1 sector too big when using the +size option).
+
+- Changed BytesToSI() to display values in bytes without decimal points
+  (e.g., "512 bytes" rather than "512.0 bytes").
+
+- Added GPTData class member functions to retrieve GPT data structure
+  locations on disk; used in my internal-use-only GPT-wiping program.
+
+- Eliminated the "a reboot is recommended" notice after writing the
+  partition table.
+
+- Added notice after sorting the partition table to the effect that
+  editing /etc/fstab and/or the boot loader files may be required.
+
+- Fixed bug in MBR-reading function that caused 0x0f (Win95 LBA) and 0x85
+  (Linux extended) extended partitions to not be read.
+
+- Fixed bug in GetLastSector() (in support.cc) that would have prevented
+  correct user entry of over-32-bit sector numbers on 32-bit systems.
+
+- Made some changes/corrections to the partition type list in
+  parttypes.cc. Most of these were based on newly-discovered MBR type
+  codes for Apple (Mac OS X) filesystems.
+
+- General code cleanup (setting explicit casts, etc.)
+
+0.2.0:
+------
+
+- Initial semi-public release
diff --git a/gptfdisk/NOTICE b/gptfdisk/NOTICE
new file mode 100644
index 0000000..623b625
--- /dev/null
+++ b/gptfdisk/NOTICE
@@ -0,0 +1,340 @@
+		    GNU GENERAL PUBLIC LICENSE
+		       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+			    Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Library General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+		    GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+			    NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+		     END OF TERMS AND CONDITIONS
+
+	    How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year  name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Library General
+Public License instead of this License.
diff --git a/gptfdisk/README b/gptfdisk/README
new file mode 100644
index 0000000..8a612ac
--- /dev/null
+++ b/gptfdisk/README
@@ -0,0 +1,272 @@
+GPT fdisk (aka gdisk, cgdisk, and sgdisk) and FixParts
+by Roderick W. Smith, rodsmith@rodsbooks.com
+
+Introduction
+------------
+
+This package includes the source code for four related disk partitioning
+programs:
+
+- gdisk -- This program is modeled after Linux fdisk, but it operates on
+  GUID Partition Table (GPT) disks rather than the Master Boot Record (MBR)
+  disks that fdisk modifies. As such, gdisk is an interactive text-mode
+  tool for manipulating partitions, but it does nothing to the contents of
+  those partitions (usually filesystems, but sometimes swap space or other
+  data).
+
+- cgdisk -- This program is modeled after Linux cfdisk, but it operates on
+  GPT disks rather than the MBR disks that cfdisk modifies. As such, cgdisk
+  is a curses-based text-mode tool for manipulating partitions, which is to
+  say that it uses an interface that relies on arrow keys and a dynamic
+  display rather than the command letters and a scrolling display like
+  gdisk uses.
+
+- sgdisk -- This program is conceptually similar to the Linux sfdisk and
+  FreeBSD gpt programs, but its operational details differ. It enables
+  manipulation of GPT disks using command-line options, so it's suitable
+  for use in scripts or by experts to perform specific tasks that might
+  take several commands in gdisk to accomplish.
+
+- fixparts -- This program, unlike the preceding three, operates on MBR
+  disks. It's intended to fix certain problems that can be created by
+  various utilities. Specifically, it can fix mis-sized extended partitions
+  and primary partitions located in the middle of extended partitions. It
+  also enables changing primary vs. logical partition status (within limits
+  of what's legal in the MBR scheme) and making a few other minor changes.
+  It does NOT support creating new partitions; for that, you should use
+  fdisk, parted, or some other tool.
+
+More details about the abilities of these tools follows.
+
+All four programs rely on the same set of underlying code base; they differ
+only in their control interfaces (defined in gdisk.cc, cgdisk.cc,
+sgdisk.cc, and fixparts.cc, respectively) and in which support code they
+use.
+
+GPT fdisk (gdisk, cgdisk, and sgdisk) Details
+---------------------------------------------
+
+The gdisk program is intended as a (somewhat) fdisk-workalike program for
+GPT-partitioned disks, cgdisk is similarly a workalike for fdisk, and
+sgdisk provides most of gdisk's functionality in a more script-friendly
+program. Although libparted and programs that use it (GNU Parted, gparted,
+etc.) provide the ability to handle GPT disks, they have certain
+limitations that gdisk overcomes. Specific advantages of gdisk, cgdisk, and
+sgdisk include:
+
+* The ability to convert MBR-partitioned disks in-place to GPT format,
+  without losing data
+
+* The ability to convert BSD disklabels in-place to create GPT
+  partitions, without losing data
+
+* The ability to convert from GPT format to MBR format without data loss
+  (gdisk and sgdisk only)
+
+* More flexible specification of filesystem type code GUIDs, which
+  GNU Parted tends to corrupt
+
+* Clear identification of the number of unallocated sectors on a
+  disk
+
+* A user interface that's familiar to long-time users of Linux
+  fdisk and cfdisk (gdisk and cgdisk only)
+
+* The MBR boot loader code is left alone
+
+* The ability to create a hybrid MBR, which permits GPT-unaware OSes to
+  access up to three GPT partitions on the disk (gdisk and sgdisk only)
+
+Of course, GPT fdisk isn't without its limitations. Most notably, it lacks
+the filesystem awareness and filesystem-related features of GParted. You
+can't resize a partition's filesystem or create a partition with a
+filesystem already in place with gdisk, for instance. There's no GUI
+version of gdisk.
+
+The GPT fdisk package provides three program files: the interactive
+text-mode gdisk, the curses-based interactive cgdisk, and the
+command-line-driven sgdisk. The first two are intended for use in manually
+partitioning disks or changing partitioning details; sgdisk is intended for
+use in scripts to help automate tasks such as disk cloning or preparing
+multiple disks for Linux installation.
+
+FixParts Details
+----------------
+
+This program's creation was motivated by cries for help I've seen in online
+forums from users who have found their partition tables to be corrupted by
+various buggy partitioning tools. Although most OSes can handle the
+afflicted disks fine, libparted-based tools (GParted, parted, most Linux
+installers, etc.) tend to flake out when presented with these disks.
+Typically, the symptom is a disk that appears to hold no partitions;
+however, sometimes the libparted tool presents partitions other than those
+that the OS sees.
+
+I've observed four causes of these symptoms, three of which FixParts can
+correct:
+
+* Old GPT data -- If a disk is used as a GPT disk and then re-used as an
+  MBR disk, the GPT data may be incompletely erased. This happens if the
+  disk is repartitioned with fdisk or the Microsoft Windows installer, for
+  instance. (Tools based on libparted correctly remove the old GPT data
+  when converting from GPT to MBR format.) FixParts checks for this problem
+  when it starts and offers to correct it. If you opt to erase the GPT
+  data, this erasure occurs immediately, unlike other changes the program
+  makes.
+
+* Mis-sized extended partitions -- Some tools create an extended partition
+  that's too large, typically ending after the last sector of the disk.
+  FixParts automatically corrects this problem (if you use the 'w' option
+  to save the partition table).
+
+* Primary partitions inside an extended partition -- Some utilities create
+  or move primary partitions to within the range covered by the extended
+  partition. FixParts can usually correct this problem by turning the
+  primary partition into a logical partition or by changing one or more
+  other logical partitions into primaries. Such corrections aren't always
+  possible, though, at least not without deleting or resizing other
+  partitions.
+
+* Leftover RAID data -- If a disk is used in a RAID array and then re-used
+  as a non-RAID disk, some utilities can become confused and fail to see
+  the disk. FixParts can NOT correct this problem. You must destroy the old
+  RAID data, or possibly remove the dmraid package from the system, to fix
+  this problem.
+
+When run, FixParts presents an fdisk-like interface, enabling you to adjust
+partition types (primary, logical, or omitted), change type codes, change
+the bootable flag, and so on. Although you can delete a partition (by
+omitting it), you can't create new partitions with the program. If you're
+used to partitioning disks, particularly with Linux fdisk, two unusual
+features of FixParts require elaboration:
+
+* No extended partitions -- Internally, FixParts reads the partition table
+  and discards data on any extended partition(s) it finds. When you save
+  the partition table, the program generates a new extended partition. This
+  design means that the program automatically corrects many problems
+  related to the extended partition. It also means that you'll see no
+  evidence of extended partitions in the FixParts user interface, although
+  it keeps track of the requirements and prevents you from creating illegal
+  layouts, such as a primary between two logicals.
+
+* Partition numbering -- In most Linux tools, partitions 1-4 are primaries
+  and partitions 5 and up are logicals. Although a legal partition table
+  loaded into FixParts will initially conform to this convention, some
+  types of damaged table might not, and various changes you make can also
+  cause deviations. When FixParts writes the partition table, its numbering
+  will be altered to conform to the standard MBR conventions, but you
+  should use the explicit labeling of partitions as primary or logical
+  rather than the partition numbers to determine a partition's status.
+
+Installing
+----------
+
+To compile GPT fdisk, you must have appropriate development tools
+installed, most notably the GNU Compiler Collection (GCC) and its g++
+compiler for C++. I've also tested compilation with Clang, which seems to
+work; however, I've not done extensive testing of the resulting binaries,
+beyond checking a few basics. Under Windows, Microsoft Visual C++ 2008 can
+be used instead. In addition, note these requirements:
+
+* On Linux, FreeBSD, OS X, and Solaris, libuuid must be installed. This is
+  the standard for Linux and OS X, although you may need to install a
+  package called uuid-dev or something similar to get the headers. On
+  FreeBSD, the e2fsprogs-libuuid port must be installed.
+
+* The ICU library (http://site.icu-project.org), which provides support for
+  Unicode partition names, is optional on all platforms except Windows, on
+  which it's not supported. Using this library was required to get proper
+  UTF-16 partition name support in GPT fdisk versions prior to 0.8.9, but
+  as of that version it should not longer be required. Nonetheless, you can
+  use it if you're having problems with the new UTF-16 support. This
+  library is normally installed in Linux and OS X, but you may need to
+  install the development headers (libicu-dev or something similar in
+  Linux; or the libicu36-dev Fink package in OS X). To compile with ICU
+  support, you must modify the Makefile: Look for commented-out lines that
+  refer to USE_UTF16, -licuuc, -licudata, or -licucore. Uncomment them and
+  comment out the equivalents that lack these lines.
+
+* The cgdisk program requires the ncurses library and its development files
+  (headers). Most Linux distributions install ncurses by default, but you
+  may need to install a package called libncurses5-dev, ncurses-devel, or
+  something similar to obtain the header files. These files were installed
+  already on my Mac OS X development system; however, they may have been
+  installed as dependencies of other programs I've installed. If you're
+  having problems installing ncurses, you can compile gdisk and/or sgdisk
+  without cgdisk by specifying only the targets you want to compile to
+  make.
+
+* The sgdisk program requires the popt library and its development files
+  (headers). Most Linux distributions install popt by default, but you may
+  need to install a package called popt-dev, popt-devel, or something
+  similar to obtain the header files. Mac OS users can find a version of
+  popt for Mac OS from Darwin Ports (http://popt.darwinports.com), MacPorts
+  (https://trac.macports.org/browser/trunk/dports/devel/popt/Portfile), or
+  Fink (http://www.finkproject.org);  however, you'll first need to install
+  DarwinPorts, MacPorts, or Fink (instructions exist on the relevant
+  projects' pages). Alternatively, you can compile gdisk and/or cgdisk
+  alone, without sgdisk; gdisk doesn't require popt.
+
+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, "make -f Makefile.freebsd" on FreeBSD, "make -f Makefile.solaris" on
+Solaris, 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, cgdisk, sgdisk,
+and fixparts. Typing "make gdisk", "make cgdisk", "make sgdisk", or "make
+fixparts" will compile only the requested programs. You can use these
+programs in place or copy the files to a suitable directory, such as
+/usr/local/sbin. You can copy the man pages (gdisk.8, cgdisk.8, sgdisk.8,
+and fixparts.8) to /usr/local/man/man8 to make them available.
+
+Caveats
+-------
+
+THIS SOFTWARE IS BETA SOFTWARE! IF IT WIPES OUT YOUR HARD DISK OR EATS YOUR
+CAT, DON'T BLAME ME! To date, I've tested the software on several USB flash
+drives, physical hard disks, and virtual disks in the QEMU and VirtualBox
+environments. Many others have now used the software on their computers, as
+well. I believe all data-corruption bugs to be squashed, but I know full well
+that the odds of my missing something are high. This is particularly true for
+large (over-2TiB) drives; my only direct testing with such disks is with
+virtual QEMU and VirtualBox disks. I've received user reports of success with
+RAID arrays over 2TiB in size, though.
+
+My main development platform is a system running the 64-bit version of
+Gentoo Linux. I've also tested on several other 32- and 64-bit Linux
+distributions, Intel-based Mac OS X 10.5 and 10.6, 64-bit FreeBSD 7.1, and
+Windows 7.
+
+Redistribution
+--------------
+
+This program is licensed under terms of the GNU GPL (see the file COPYING).
+
+Acknowledgements
+----------------
+
+This code is mostly my own; however, I've used three functions from two
+other GPLed programs:
+
+- The code used to generate CRCs is taken from the efone program by
+  Krzysztof Dabrowski and ElysiuM deeZine. (See the crc32.h and
+  crc32.cc source code files.)
+
+- A function to find the disk size is taken from Linux fdisk by A. V. Le
+  Blanc. This code has subsequently been heavily modified.
+
+Additional code contributors include:
+
+- Yves Blusseau (1otnwmz02@sneakemail.com)
+
+- David Hubbard (david.c.hubbard@gmail.com)
+
+- Justin Maggard (justin.maggard@netgear.com)
+
+- Dwight Schauer (dschauer@ti.com)
+
+- Florian Zumbiehl (florz@florz.de)
+
+- Guillaume Delacour (contributed the gdisk_test.sh script)
diff --git a/gptfdisk/README.Windows b/gptfdisk/README.Windows
new file mode 100644
index 0000000..3f49023
--- /dev/null
+++ b/gptfdisk/README.Windows
@@ -0,0 +1,136 @@
+GPT fdisk (aka gdisk) and FixParts

+

+by Roderick W. Smith, rodsmith@rodsbooks.com

+

+******************************** IMPORTANT ********************************

+Most versions of Windows cannot boot from a GPT disk on BIOS-based

+computers, and most varieties prior to Vista cannot read GPT disks. GPT

+fdisk is a partition editor for GPT disks, and it will *AUTOMATICALLY

+CONVERT* MBR disks to GPT form. Therefore, you should **NOT** use GPT fdisk

+on a Windows system unless you fully understand what you're doing or are

+certain that your computer boots in EFI/UEFI mode! If you accidentally use

+GPT fdisk on a BIOS-mode boot disk, or perhaps even on a data disk, you may

+find recovery to be very difficult! Pre-installed Windows 8 and later

+systems almost always use GPT disks and boot in EFI/UEFI mode, but

+self-installed Windows 8 systems sometimes use BIOS mode. This caveat does

+not apply to FixParts, though; that tool works only on MBR disks.

+***************************************************************************

+

+Read the main README file for general information on the program, and read

+the gdisk.html or fixparts.html documents (the Linux man pages 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. I originally wrote GPT fdisk on Linux, and some Linux- and

+Unix-centric language remains in the documentation.

+

+Windows Use Notes

+-----------------

+

+The Windows version of GPT fdisk was added with version 0.6.2 of the

+package. The Windows binary package includes the gdisk.exe interactive

+text-mode program file but no equivalent to the sgdisk program that's

+available with Linux, FreeBSD, and OS X builds. In theory, an sgdisk.exe

+for Windows could be built if the popt library were installed. I've not

+attempted to do this myself, though. If you care to try, check

+http://gnuwin32.sourceforge.net/packages/popt.htm for information on popt

+for Windows.

+

+Beginning with version 0.8.10, I'm distributing both 32-bit and 64-bit

+binaries, which include the strings "32" or "64" in their names. The 32-bit

+binaries work fine on most versions of Windows, but some 64-bit

+installations of Windows 8 lack 32-bit support libraries and so may need

+the 64-bit binaries.

+

+The FixParts program (fixparts32.exe and fixparts64.exe) is new with GPT

+fdisk 0.7.0. As described in the main README file, this program fixes

+certain partition table problems that can be created by buggy partitioning

+software. Windows seems to be unfazed by most such problems, but I've not

+done an extensive survey of Windows partitioning tools on this score.

+

+To install the programs, copy the gdisk32.exe and fixparts32.exe (or

+gdisk64.exe and fixparts64.exe) program files to any directory on your

+path, such as C:\Windows. Alternatively, you can change to the program's

+directory or type its complete path whenever you use it.

+

+To use the programs, first launch a Command Prompt as the Administrator. To

+do this, locate the Command Prompt program icon, right-click it, and select

+"Run as Administrator." If you use a non-Administrator Command Prompt, you

+won't be able to edit hard disk partition tables, although you will be able

+to edit raw disk image files.

+

+The program requires a hard disk identifier as an option. You can specify

+this in either of two forms. The first way is as a number followed by a

+colon, as in:

+

+gdisk 0:

+

+Disks are numbered starting from 0, so the preceding command launches gdisk

+on the first disk. The second way to specify a disk device is via a

+harder-to-remember name:

+

+gdisk32 \\.\physicaldrive0

+

+This command is equivalent to the earlier one -- it edits the partition

+table on the first physical disk. Change the number at the end of the

+device name to change the disk edited.

+

+If you pass the "-l" option to gdisk.exe in addition to the disk

+identifier, the program displays the current partition table information

+and then exits. This use entails no risk to MBR disks, since the program

+never writes data back to the disk when used in this way.

+

+As noted above, editing the first disk with GPT fdisk is usually a Bad

+Idea. An exception would be if your system uses an Extensible Firmware

+Interface (EFI) and already boots from a GPT disk. It's safer to edit

+non-boot disks, which usually have numbers of 1 and above, but only if you

+run a version of Windows with GPT support. For more information on Windows'

+support of GPT, see Microsoft's Web page on the topic:

+

+http://www.microsoft.com/whdc/device/storage/GPT_FAQ.mspx

+

+The Windows binaries I've compiled do not support Unicode UTF-16LE GPT

+partition names. This feature was added to version 0.7.1 of the software

+for Linux, FreeBSD, and OS X, and with changes to some #ifndef lines in the

+source files, it can be compiled for Windows; however, it seems to do

+little good in Windows because of Command Prompt window and/or ICU library

+limitations. Thus, I've omitted this support in the interests of

+simplifying the binary distribution, since including it would mean

+distributing the ICU libraries.

+

+Source Code and Compilation Issues

+----------------------------------

+

+I have successfully compiled GPT fdisk using three different Windows

+compilers:

+

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

+  cross-compiler -- Under Ubuntu Linux, the Makefile.mingw and

+  Makefile.mingw64 files enable compilation of the software via MinGW.

+  (Type "make -f Makefile.mingw" to compile 32-bit binaries, and "make -f

+  Makefile.mingw64" to compile 64-bit binaries.) 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.

+

+- 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, sgdisk.cc, and whichever

+  program file you intend to NOT build (gdisk.cc or fixparts.cc).

+

+- Microsoft Visual C++ 2010 Express -- This compiler works much like the

+  2008 version, although I didn't need to add a third-party stdint.h file.

+

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

+compilers. 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/gptfdisk/android_popt.cc b/gptfdisk/android_popt.cc
new file mode 100644
index 0000000..a1492c6
--- /dev/null
+++ b/gptfdisk/android_popt.cc
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <popt.h>
+
+// #define LOCAL_DEBUG
+
+/*
+ * popt has been deprecated for some time, and is replaced by GNOME's glib
+ * option parser. Instead of pulling in either of those dependencies, this
+ * stub implements just enough of popt to get things working.
+ */
+
+poptContext poptGetContext(const char *name, int argc, const char **argv,
+        const struct poptOption *options, unsigned int flags) {
+    // Convert into getopt format, sanity checking our limited
+    // capabilities along the way
+    int count = 0;
+    for (; options[count].longName; count++) {
+    }
+
+    struct option *long_options = (struct option *)
+            calloc(count, sizeof(struct option));
+    for (int i = 0; options[i].longName; i++) {
+        long_options[i].name = options[i].longName;
+        long_options[i].flag = 0;
+
+        if (!options[i].val) {
+            fprintf(stderr, __FILE__ ": val required\n");
+            abort();
+        }
+        long_options[i].val = options[i].val;
+
+        switch (options[i].argInfo) {
+        case POPT_ARG_NONE:
+            long_options[i].has_arg = no_argument;
+            break;
+        case POPT_ARG_STRING:
+        case POPT_ARG_INT:
+            if (!options[i].arg) {
+                fprintf(stderr, __FILE__ ": arg required\n");
+                abort();
+            }
+            long_options[i].has_arg = required_argument;
+            break;
+        default:
+            fprintf(stderr, __FILE__ ": unsupported argInfo\n");
+            abort();
+        }
+    }
+
+    poptContext con = (poptContext) calloc(1, sizeof(struct _poptContext));
+    con->argc = argc;
+    con->argv = argv;
+    con->options = options;
+    con->long_options = long_options;
+    return con;
+}
+
+poptContext poptFreeContext(poptContext con) {
+    free(con->long_options);
+    free(con);
+    return 0;
+}
+
+void poptResetContext(poptContext con) {
+    optind = 1;
+}
+
+void poptSetOtherOptionHelp(poptContext con, const char *text) {
+    con->otherHelp = text;
+}
+
+void poptPrintUsage(poptContext con, FILE *fp, int flags) {
+    fprintf(fp, "USAGE: %s %s\n", con->argv[0], con->otherHelp);
+    int i = 0;
+    for (; con->options[i].longName; i++) {
+        fprintf(fp, "\t--%s\t%s\n", con->options[i].longName,
+                con->options[i].descrip);
+    }
+    fprintf(fp, "\n");
+}
+
+int poptGetNextOpt(poptContext con) {
+    int i = -1;
+    int res = getopt_long(con->argc, (char *const *) con->argv, "",
+            con->long_options, &i);
+#ifdef LOCAL_DEBUG
+    fprintf(stderr, "getopt_long()=%c\n", res);
+#endif
+    if (res == 0 || res == '?') {
+        return -1;
+    }
+
+    // Copy over found argument value
+    switch (con->options[i].argInfo) {
+    case POPT_ARG_STRING:
+        *((char**) con->options[i].arg) = strdup(optarg);
+        break;
+    case POPT_ARG_INT:
+        *((int*) con->options[i].arg) = atoi(optarg);
+        break;
+    }
+
+    return res;
+}
+
+const char *poptGetArg(poptContext con) {
+    const char *res = con->argv[optind++];
+#ifdef LOCAL_DEBUG
+    fprintf(stderr, "poptGetArg()=%s\n", res);
+#endif
+    return res;
+}
diff --git a/gptfdisk/attributes.cc b/gptfdisk/attributes.cc
new file mode 100644
index 0000000..bbd5aad
--- /dev/null
+++ b/gptfdisk/attributes.cc
@@ -0,0 +1,228 @@
+// attributes.cc
+// Class to manage partition attribute codes. These are binary bit fields,
+// of which only four are currently (2/2011) documented on Wikipedia, and
+// two others found from other sources.
+
+/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <stdint.h>
+#include <stdio.h>
+#include <iostream>
+#include <sstream>
+
+#include "attributes.h"
+#include "support.h"
+
+using namespace std;
+
+string Attributes::atNames[NUM_ATR];
+int Attributes::numAttrs = 0;
+//Attributes::staticInit Attributes::staticInitializer;
+
+// Default constructor
+Attributes::Attributes(void) {
+   numAttrs++;
+   if (numAttrs == 1)
+      Setup();
+   attributes = 0;
+} // constructor
+
+// Alternate constructor
+Attributes::Attributes(const uint64_t a) {
+   numAttrs++;
+   if (numAttrs == 1)
+      Setup();
+   attributes = a;
+} // alternate constructor
+
+// Destructor.
+Attributes::~Attributes(void) {
+   numAttrs--;
+} // Attributes destructor
+
+void Attributes::Setup(void) {
+   ostringstream temp;
+
+   // Most bits are undefined, so start by giving them an
+   // appropriate name
+   for (int i = 0; i < NUM_ATR; i++) {
+      temp.str("");
+      temp << "Undefined bit #" << i;
+      Attributes::atNames[i] = temp.str();
+   } // for
+
+   // Now reset those names that are defined....
+   atNames[0] = "system partition"; // required for computer to operate
+   atNames[1] = "hide from EFI";
+   atNames[2] = "legacy BIOS bootable";
+   atNames[60] = "read-only";
+   atNames[62] = "hidden";
+   atNames[63] = "do not automount";
+}  // Attributes::Setup()
+
+// Display current attributes to user
+void Attributes::DisplayAttributes(void) {
+   uint32_t i;
+   int numSet = 0;
+
+   cout << "Attribute value is ";
+   cout.setf(ios::uppercase);
+   cout.fill('0');
+   cout.width(16);
+   cout << hex << attributes << dec << ". Set fields are:\n";
+   for (i = 0; i < NUM_ATR; i++) {
+      if ((UINT64_C(1) << i) & attributes) {
+         cout << i << " (" << GetAttributeName(i) << ")" << "\n";
+         numSet++;
+      } // if
+   } // for
+   cout.fill(' ');
+   if (numSet == 0)
+      cout << "  No fields set\n";
+   cout << "\n";
+} // Attributes::DisplayAttributes()
+
+// Display attributes for a partition. Note that partNum is just passed for
+// immediate display; it's not used to access a particular partition.
+void Attributes::ShowAttributes(const uint32_t partNum) {
+   uint32_t bitNum;
+   bool bitset;
+
+   for (bitNum = 0; bitNum < 64; bitNum++) {
+      bitset = (UINT64_C(1) << bitNum) & attributes;
+      if (bitset) {
+         cout << partNum+1 << ":" << bitNum << ":" << bitset
+         << " (" << GetAttributeName(bitNum) << ")" << endl;
+      } // if
+   } // for
+} // Attributes::ShowAttributes
+
+// Prompt user for attribute changes
+void Attributes::ChangeAttributes(void) {
+   int response;
+   uint64_t bitValue;
+
+   cout << "Known attributes are:\n";
+   ListAttributes();
+   cout << "\n";
+
+   do {
+      DisplayAttributes();
+      response = GetNumber(0, NUM_ATR, 64,
+                           "Toggle which attribute field (0-63, 64 or <Enter> to exit): ");
+      if (response != 64) {
+         bitValue = UINT64_C(1) << response; // Find the integer value of the bit
+         if (bitValue & attributes) { // bit is set
+            attributes &= ~bitValue; // so unset it
+	         cout << "Have disabled the '" << atNames[response] << "' attribute.\n";
+         } else { // bit is not set
+            attributes |= bitValue; // so set it
+            cout << "Have enabled the '" << atNames[response] << "' attribute.\n";
+         } // if/else
+      } // if
+   } while (response != 64);
+} // Attributes::ChangeAttributes()
+
+// Display all defined attributes on the screen (omits undefined bits).
+void Attributes::ListAttributes(void) {
+   uint32_t bitNum;
+   string tempAttr;
+
+   for (bitNum = 0; bitNum < NUM_ATR; bitNum++) {
+      tempAttr = GetAttributeName(bitNum);
+      if (tempAttr.substr(0, 15) != "Undefined bit #" )
+         cout << bitNum << ": " << Attributes::GetAttributeName(bitNum) << "\n";
+   } // for
+} // Attributes::ListAttributes
+
+// multifaceted attributes access
+// returns true upon success, false upon failure
+bool Attributes::OperateOnAttributes(const uint32_t partNum, const string& attributeOperator, const string& attributeBits) {
+
+   // attribute access opcode
+   typedef enum {
+      ao_or, ao_nand, ao_xor, ao_assignall,  // operate on all attributes (bitmask)
+      ao_unknown, // must be after bitmask operators and before bitnum operators
+      ao_set, ao_clear, ao_toggle, ao_get    // operate on a single attribute (bitnum)
+   } attribute_opcode_t; // typedef enum
+
+   // translate attribute operator into an attribute opcode
+   attribute_opcode_t attributeOpcode = ao_unknown; { // opcode is not known yet
+      if      (attributeOperator == "or")      attributeOpcode = ao_or;
+      else if (attributeOperator == "nand")    attributeOpcode = ao_nand;
+      else if (attributeOperator == "xor")     attributeOpcode = ao_xor;
+      else if (attributeOperator == "=")       attributeOpcode = ao_assignall;
+      else if (attributeOperator == "set")     attributeOpcode = ao_set;
+      else if (attributeOperator == "clear")   attributeOpcode = ao_clear;
+      else if (attributeOperator == "toggle")  attributeOpcode = ao_toggle;
+      else if (attributeOperator == "get")     attributeOpcode = ao_get;
+      else {
+         cerr << "Unknown attributes operator: " << attributeOperator << endl;
+         return false;
+      } // else
+   } // attributeOpcode
+
+   // get bit mask if operating on entire attribute set
+   uint64_t attributeBitMask; { if (attributeOpcode < ao_unknown) {
+      if (1 != sscanf (attributeBits.c_str(), "%qx", (long long unsigned int*) &attributeBitMask)) {
+         cerr << "Could not convert hex attribute mask" << endl;
+         return false;
+      } // if
+   }} // attributeBitMask, if
+
+   // get bit number and calculate bit mask if operating on a single attribute
+   int bitNum; { if (attributeOpcode > ao_unknown) {
+      if (1 != sscanf (attributeBits.c_str(), "%d", &bitNum)) {
+         cerr << "Could not convert bit number" << endl;
+         return false;
+      } // if
+      const uint64_t one = 1;
+      attributeBitMask = one << bitNum;
+   }} // bitNum, if
+
+   switch (attributeOpcode) {
+      // assign all attributes at once
+      case ao_assignall:  attributes = attributeBitMask;    break;
+
+      // set individual attribute(s)
+      case ao_set:
+      case ao_or:         attributes |= attributeBitMask;   break;
+
+      // clear individual attribute(s)
+      case ao_clear:
+      case ao_nand:       attributes &= ~attributeBitMask;  break;
+
+      // toggle individual attribute(s)
+      case ao_toggle:
+      case ao_xor:        attributes ^= attributeBitMask;   break;
+
+      // display a single attribute
+      case ao_get: {
+         cout << partNum+1 << ":" << bitNum << ":"
+              << bool (attributeBitMask & attributes) << endl;
+         break;
+      } // case ao_get
+
+      default: break; // will never get here
+   } // switch
+
+   return true;
+} // Attributes::OperateOnAttributes()
+
+/*******************************
+*                             *
+* Non-class support functions *
+*                             *
+*******************************/
+
+// Display attributes
+ostream & operator<<(ostream & os, const Attributes & data) {
+   os << data.GetAttributes();
+   return os;
+} // operator<<()
diff --git a/gptfdisk/attributes.h b/gptfdisk/attributes.h
new file mode 100644
index 0000000..f6c66ff
--- /dev/null
+++ b/gptfdisk/attributes.h
@@ -0,0 +1,41 @@
+/* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#include <stdint.h>
+#include <string>
+
+#ifndef __GPT_ATTRIBUTES
+#define __GPT_ATTRIBUTES
+
+#define NUM_ATR 64 /* # of attributes -- 64, since it's a 64-bit field */
+#define ATR_NAME_SIZE 25 /* maximum size of attribute names */
+
+using namespace std;
+
+class Attributes {
+protected:
+   static string atNames[NUM_ATR];
+   static int numAttrs;
+   void Setup(void);
+   uint64_t attributes;
+
+public:
+   Attributes(void);
+   Attributes(const uint64_t a);
+   ~Attributes(void);
+   void operator=(uint64_t a) {attributes = a;}
+
+   uint64_t GetAttributes(void) const {return attributes;}
+   void DisplayAttributes(void);
+   void ShowAttributes(const uint32_t partNum);
+
+   void ChangeAttributes(void);
+   bool OperateOnAttributes(const uint32_t partNum, const string& attributeOperator, const string& attributeBits);
+
+   static const string& GetAttributeName(const uint32_t bitNum) {return atNames [bitNum];}
+   static void ListAttributes(void);
+}; // class Attributes
+
+ostream & operator<<(ostream & os, const Attributes & data);
+
+#endif
diff --git a/gptfdisk/basicmbr.cc b/gptfdisk/basicmbr.cc
new file mode 100644
index 0000000..c3c0653
--- /dev/null
+++ b/gptfdisk/basicmbr.cc
@@ -0,0 +1,1639 @@
+/* basicmbr.cc -- Functions for loading, saving, and manipulating legacy MBR partition
+   data. */
+
+/* Initial coding by Rod Smith, January to February, 2009 */
+
+/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <iostream>
+#include <algorithm>
+#include "mbr.h"
+#include "support.h"
+
+using namespace std;
+
+/****************************************
+ *                                      *
+ * MBRData class and related structures *
+ *                                      *
+ ****************************************/
+
+BasicMBRData::BasicMBRData(void) {
+   blockSize = SECTOR_SIZE;
+   diskSize = 0;
+   device = "";
+   state = invalid;
+   numHeads = MAX_HEADS;
+   numSecspTrack = MAX_SECSPERTRACK;
+   myDisk = NULL;
+   canDeleteMyDisk = 0;
+//   memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t));
+   EmptyMBR();
+} // BasicMBRData default constructor
+
+BasicMBRData::BasicMBRData(string filename) {
+   blockSize = SECTOR_SIZE;
+   diskSize = 0;
+   device = filename;
+   state = invalid;
+   numHeads = MAX_HEADS;
+   numSecspTrack = MAX_SECSPERTRACK;
+   myDisk = NULL;
+   canDeleteMyDisk = 0;
+//   memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint32_t));
+   
+   // Try to read the specified partition table, but if it fails....
+   if (!ReadMBRData(filename)) {
+      EmptyMBR();
+      device = "";
+   } // if
+} // BasicMBRData(string filename) constructor
+
+// Free space used by myDisk only if that's OK -- sometimes it will be
+// copied from an outside source, in which case that source should handle
+// it!
+BasicMBRData::~BasicMBRData(void) {
+   if (canDeleteMyDisk)
+      delete myDisk;
+} // BasicMBRData destructor
+
+// Assignment operator -- copy entire set of MBR data.
+BasicMBRData & BasicMBRData::operator=(const BasicMBRData & orig) {
+   int i;
+
+   memcpy(code, orig.code, 440);
+   diskSignature = orig.diskSignature;
+   nulls = orig.nulls;
+   MBRSignature = orig.MBRSignature;
+   blockSize = orig.blockSize;
+   diskSize = orig.diskSize;
+   numHeads = orig.numHeads;
+   numSecspTrack = orig.numSecspTrack;
+   canDeleteMyDisk = orig.canDeleteMyDisk;
+   device = orig.device;
+   state = orig.state;
+
+   myDisk = new DiskIO;
+   if (myDisk == NULL) {
+      cerr << "Unable to allocate memory in BasicMBRData::operator=()! Terminating!\n";
+      exit(1);
+   } // if
+   if (orig.myDisk != NULL)
+      myDisk->OpenForRead(orig.myDisk->GetName());
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      partitions[i] = orig.partitions[i];
+   } // for
+   return *this;
+} // BasicMBRData::operator=()
+
+/**********************
+ *                    *
+ * Disk I/O functions *
+ *                    *
+ **********************/
+
+// Read data from MBR. Returns 1 if read was successful (even if the
+// data isn't a valid MBR), 0 if the read failed.
+int BasicMBRData::ReadMBRData(const string & deviceFilename) {
+   int allOK = 1;
+
+   if (myDisk == NULL) {
+      myDisk = new DiskIO;
+      if (myDisk == NULL) {
+         cerr << "Unable to allocate memory in BasicMBRData::ReadMBRData()! Terminating!\n";
+         exit(1);
+      } // if
+      canDeleteMyDisk = 1;
+   } // if
+   if (myDisk->OpenForRead(deviceFilename)) {
+      allOK = ReadMBRData(myDisk);
+   } else {
+      allOK = 0;
+   } // if
+
+   if (allOK)
+      device = deviceFilename;
+
+   return allOK;
+} // BasicMBRData::ReadMBRData(const string & deviceFilename)
+
+// Read data from MBR. If checkBlockSize == 1 (the default), the block
+// size is checked; otherwise it's set to the default (512 bytes).
+// Note that any extended partition(s) present will be omitted from
+// in the partitions[] array; these partitions must be re-created when
+// the partition table is saved in MBR format.
+int BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize) {
+   int allOK = 1, i, logicalNum = 3;
+   int err = 1;
+   TempMBR tempMBR;
+
+   if ((myDisk != NULL) && (myDisk != theDisk) && (canDeleteMyDisk)) {
+      delete myDisk;
+      canDeleteMyDisk = 0;
+   } // if
+
+   myDisk = theDisk;
+
+   // Empty existing MBR data, including the logical partitions...
+   EmptyMBR(0);
+
+   if (myDisk->Seek(0))
+     if (myDisk->Read(&tempMBR, 512))
+        err = 0;
+   if (err) {
+      cerr << "Problem reading disk in BasicMBRData::ReadMBRData()!\n";
+   } else {
+      for (i = 0; i < 440; i++)
+         code[i] = tempMBR.code[i];
+      diskSignature = tempMBR.diskSignature;
+      nulls = tempMBR.nulls;
+      for (i = 0; i < 4; i++) {
+         partitions[i] = tempMBR.partitions[i];
+         if (partitions[i].GetLengthLBA() > 0)
+            partitions[i].SetInclusion(PRIMARY);
+      } // for i... (reading all four partitions)
+      MBRSignature = tempMBR.MBRSignature;
+      ReadCHSGeom();
+
+      // Reverse the byte order, if necessary
+      if (IsLittleEndian() == 0) {
+         ReverseBytes(&diskSignature, 4);
+         ReverseBytes(&nulls, 2);
+         ReverseBytes(&MBRSignature, 2);
+         for (i = 0; i < 4; i++) {
+            partitions[i].ReverseByteOrder();
+         } // for
+      } // if
+
+      if (MBRSignature != MBR_SIGNATURE) {
+         allOK = 0;
+         state = invalid;
+      } // if
+
+      // Find disk size
+      diskSize = myDisk->DiskSize(&err);
+
+      // Find block size
+      if (checkBlockSize) {
+         blockSize = myDisk->GetBlockSize();
+      } // if (checkBlockSize)
+
+      // Load logical partition data, if any is found....
+      if (allOK) {
+         for (i = 0; i < 4; i++) {
+            if ((partitions[i].GetType() == 0x05) || (partitions[i].GetType() == 0x0f)
+                || (partitions[i].GetType() == 0x85)) {
+               // Found it, so call a function to load everything from them....
+               logicalNum = ReadLogicalParts(partitions[i].GetStartLBA(), abs(logicalNum) + 1);
+               if (logicalNum < 0) {
+                  cerr << "Error reading logical partitions! List may be truncated!\n";
+               } // if maxLogicals valid
+               DeletePartition(i);
+            } // if primary partition is extended
+         } // for primary partition loop
+         if (allOK) { // Loaded logicals OK
+            state = mbr;
+         } else {
+            state = invalid;
+         } // if
+      } // if
+
+      // Check to see if it's in GPT format....
+      if (allOK) {
+         for (i = 0; i < 4; i++) {
+            if (partitions[i].GetType() == UINT8_C(0xEE)) {
+               state = gpt;
+            } // if
+         } // for
+      } // if
+
+      // If there's an EFI GPT partition, look for other partition types,
+      // to flag as hybrid
+      if (state == gpt) {
+         for (i = 0 ; i < 4; i++) {
+            if ((partitions[i].GetType() != UINT8_C(0xEE)) &&
+                (partitions[i].GetType() != UINT8_C(0x00)))
+               state = hybrid;
+            if (logicalNum != 3)
+               cerr << "Warning! MBR Logical partitions found on a hybrid MBR disk! This is an\n"
+                    << "EXTREMELY dangerous configuration!\n\a";
+         } // for
+      } // if (hybrid detection code)
+   } // no initial error
+   return allOK;
+} // BasicMBRData::ReadMBRData(DiskIO * theDisk, int checkBlockSize)
+
+// This is a function to read all the logical partitions, following the
+// logical partition linked list from the disk and storing the basic data in the
+// partitions[] array. Returns last index to partitions[] used, or -1 times the
+// that index if there was a problem. (Some problems can leave valid logical
+// partition data.)
+// Parameters:
+// extendedStart = LBA of the start of the extended partition
+// partNum = number of first partition in extended partition (normally 4).
+int BasicMBRData::ReadLogicalParts(uint64_t extendedStart, int partNum) {
+   struct TempMBR ebr;
+   int i, another = 1, allOK = 1;
+   uint8_t ebrType;
+   uint64_t offset;
+   uint64_t EbrLocations[MAX_MBR_PARTS];
+
+   offset = extendedStart;
+   memset(&EbrLocations, 0, MAX_MBR_PARTS * sizeof(uint64_t));
+   while (another && (partNum < MAX_MBR_PARTS) && (partNum >= 0) && (allOK > 0)) {
+      for (i = 0; i < MAX_MBR_PARTS; i++) {
+         if (EbrLocations[i] == offset) { // already read this one; infinite logical partition loop!
+            cerr << "Logical partition infinite loop detected! This is being corrected.\n";
+            allOK = -1;
+            partNum -= 1;
+         } // if
+      } // for
+      EbrLocations[partNum] = offset;
+      if (myDisk->Seek(offset) == 0) { // seek to EBR record
+         cerr << "Unable to seek to " << offset << "! Aborting!\n";
+         allOK = -1;
+      }
+      if (myDisk->Read(&ebr, 512) != 512) { // Load the data....
+         cerr << "Error seeking to or reading logical partition data from " << offset
+              << "!\nSome logical partitions may be missing!\n";
+         allOK = -1;
+      } else if (IsLittleEndian() != 1) { // Reverse byte ordering of some data....
+         ReverseBytes(&ebr.MBRSignature, 2);
+         ReverseBytes(&ebr.partitions[0].firstLBA, 4);
+         ReverseBytes(&ebr.partitions[0].lengthLBA, 4);
+         ReverseBytes(&ebr.partitions[1].firstLBA, 4);
+         ReverseBytes(&ebr.partitions[1].lengthLBA, 4);
+      } // if/else/if
+
+      if (ebr.MBRSignature != MBR_SIGNATURE) {
+         allOK = -1;
+         cerr << "EBR signature for logical partition invalid; read 0x";
+         cerr.fill('0');
+         cerr.width(4);
+         cerr.setf(ios::uppercase);
+         cerr << hex << ebr.MBRSignature << ", but should be 0x";
+         cerr.width(4);
+         cerr << MBR_SIGNATURE << dec << "\n";
+         cerr.fill(' ');
+      } // if
+
+      if ((partNum >= 0) && (partNum < MAX_MBR_PARTS) && (allOK > 0)) {
+         // Sometimes an EBR points directly to another EBR, rather than defining
+         // a logical partition and then pointing to another EBR. Thus, we skip
+         // the logical partition when this is the case....
+         ebrType = ebr.partitions[0].partitionType;
+         if ((ebrType == 0x05) || (ebrType == 0x0f) || (ebrType == 0x85)) {
+            cout << "EBR points to an EBR!\n";
+            offset = extendedStart + ebr.partitions[0].firstLBA;
+         } else {
+            // Copy over the basic data....
+            partitions[partNum] = ebr.partitions[0];
+            // Adjust the start LBA, since it's encoded strangely....
+            partitions[partNum].SetStartLBA(ebr.partitions[0].firstLBA + offset);
+            partitions[partNum].SetInclusion(LOGICAL);
+            
+            // Find the next partition (if there is one)
+            if ((ebr.partitions[1].firstLBA != UINT32_C(0)) && (partNum < (MAX_MBR_PARTS - 1))) {
+               offset = extendedStart + ebr.partitions[1].firstLBA;
+               partNum++;
+            } else {
+               another = 0;
+            } // if another partition
+         } // if/else
+      } // if
+   } // while()
+   return (partNum * allOK);
+} // BasicMBRData::ReadLogicalPart()
+
+// 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 BasicMBRData::WriteMBRData(void) {
+   int allOK = 1;
+
+   if (myDisk != NULL) {
+      if (myDisk->OpenForWrite() != 0) {
+         allOK = WriteMBRData(myDisk);
+         cout << "Done writing data!\n";
+      } else {
+         allOK = 0;
+      } // if/else
+      myDisk->Close();
+   } else allOK = 0;
+   return allOK;
+} // BasicMBRData::WriteMBRData(void)
+
+// Save the MBR data to a file. This writes both the
+// MBR itself and any defined logical partitions.
+int BasicMBRData::WriteMBRData(DiskIO *theDisk) {
+   int i, j, partNum, next, allOK = 1, moreLogicals = 0;
+   uint64_t extFirstLBA = 0;
+   uint64_t writeEbrTo; // 64-bit because we support extended in 2-4TiB range
+   TempMBR tempMBR;
+
+   allOK = CreateExtended();
+   if (allOK) {
+      // First write the main MBR data structure....
+      memcpy(tempMBR.code, code, 440);
+      tempMBR.diskSignature = diskSignature;
+      tempMBR.nulls = nulls;
+      tempMBR.MBRSignature = MBRSignature;
+      for (i = 0; i < 4; i++) {
+         partitions[i].StoreInStruct(&tempMBR.partitions[i]);
+         if (partitions[i].GetType() == 0x0f) {
+            extFirstLBA = partitions[i].GetStartLBA();
+            moreLogicals = 1;
+         } // if
+      } // for i...
+   } // if
+   allOK = allOK && WriteMBRData(tempMBR, theDisk, 0);
+
+   // 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 = FindNextInUse(4);
+   writeEbrTo = (uint64_t) extFirstLBA;
+   // Write logicals...
+   while (allOK && moreLogicals && (partNum < MAX_MBR_PARTS) && (partNum >= 0)) {
+      partitions[partNum].StoreInStruct(&tempMBR.partitions[0]);
+      tempMBR.partitions[0].firstLBA = 1;
+      // tempMBR.partitions[1] points to next EBR or terminates EBR linked list...
+      next = FindNextInUse(partNum + 1);
+      if ((next < MAX_MBR_PARTS) && (next > 0) && (partitions[next].GetStartLBA() > 0)) {
+         tempMBR.partitions[1].partitionType = 0x0f;
+         tempMBR.partitions[1].firstLBA = (uint32_t) (partitions[next].GetStartLBA() - extFirstLBA - 1);
+         tempMBR.partitions[1].lengthLBA = (uint32_t) (partitions[next].GetLengthLBA() + 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);
+      writeEbrTo = (uint64_t) tempMBR.partitions[1].firstLBA + (uint64_t) extFirstLBA;
+      partNum = next;
+   } // while
+   DeleteExtendedParts();
+   return allOK;
+} // BasicMBRData::WriteMBRData(DiskIO *theDisk)
+
+int BasicMBRData::WriteMBRData(const string & deviceFilename) {
+   device = deviceFilename;
+   return WriteMBRData();
+} // BasicMBRData::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 BasicMBRData::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;
+} // BasicMBRData::WriteMBRData(uint64_t sector)
+
+// Set a new disk device; used in copying one disk's partition
+// table to another disk.
+void BasicMBRData::SetDisk(DiskIO *theDisk) {
+   int err;
+
+   myDisk = theDisk;
+   diskSize = theDisk->DiskSize(&err);
+   canDeleteMyDisk = 0;
+   ReadCHSGeom();
+} // BasicMBRData::SetDisk()
+
+/********************************************
+ *                                          *
+ * Functions that display data for the user *
+ *                                          *
+ ********************************************/
+
+// Show the MBR data to the user, up to the specified maximum number
+// of partitions....
+void BasicMBRData::DisplayMBRData(void) {
+   int i;
+
+   cout << "\nDisk size is " << diskSize << " sectors ("
+        << BytesToIeee(diskSize, blockSize) << ")\n";
+   cout << "MBR disk identifier: 0x";
+   cout.width(8);
+   cout.fill('0');
+   cout.setf(ios::uppercase);
+   cout << hex << diskSignature << dec << "\n";
+   cout << "MBR partitions:\n\n";
+   if ((state == gpt) || (state == hybrid)) {
+      cout << "Number  Boot  Start Sector   End Sector   Status      Code\n";
+   } else {
+      cout << "                                                   Can Be   Can Be\n";
+      cout << "Number  Boot  Start Sector   End Sector   Status   Logical  Primary   Code\n";
+      UpdateCanBeLogical();
+   } // 
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if (partitions[i].GetLengthLBA() != 0) {
+         cout.fill(' ');
+         cout.width(4);
+         cout << i + 1 << "      ";
+         partitions[i].ShowData((state == gpt) || (state == hybrid));
+      } // if
+      cout.fill(' ');
+   } // for
+} // BasicMBRData::DisplayMBRData()
+
+// Displays the state, as a word, on stdout. Used for debugging & to
+// tell the user about the MBR state when the program launches....
+void BasicMBRData::ShowState(void) {
+   switch (state) {
+      case invalid:
+         cout << "  MBR: not present\n";
+         break;
+      case gpt:
+         cout << "  MBR: protective\n";
+         break;
+      case hybrid:
+         cout << "  MBR: hybrid\n";
+         break;
+      case mbr:
+         cout << "  MBR: MBR only\n";
+         break;
+      default:
+         cout << "\a  MBR: unknown -- bug!\n";
+         break;
+   } // switch
+} // BasicMBRData::ShowState()
+
+/************************
+ *                      *
+ * GPT Checks and fixes *
+ *                      *
+ ************************/
+
+// Perform a very rudimentary check for GPT data on the disk; searches for
+// the GPT signature in the main and backup metadata areas.
+// Returns 0 if GPT data not found, 1 if main data only is found, 2 if
+// backup only is found, 3 if both main and backup data are found, and
+// -1 if a disk error occurred.
+int BasicMBRData::CheckForGPT(void) {
+   int retval = 0, err;
+   char signature1[9], signature2[9];
+
+   if (myDisk != NULL) {
+      if (myDisk->OpenForRead() != 0) {
+         if (myDisk->Seek(1)) {
+            myDisk->Read(signature1, 8);
+            signature1[8] = '\0';
+         } else retval = -1;
+         if (myDisk->Seek(myDisk->DiskSize(&err) - 1)) {
+            myDisk->Read(signature2, 8);
+            signature2[8] = '\0';
+         } else retval = -1;
+         if ((retval >= 0) && (strcmp(signature1, "EFI PART") == 0))
+            retval += 1;
+         if ((retval >= 0) && (strcmp(signature2, "EFI PART") == 0))
+            retval += 2;
+      } else {
+         retval = -1;
+      } // if/else
+      myDisk->Close();
+   } else retval = -1;
+   return retval;
+} // BasicMBRData::CheckForGPT()
+
+// Blanks the 2nd (sector #1, numbered from 0) and last sectors of the disk,
+// but only if GPT data are verified on the disk, and only for the sector(s)
+// with GPT signatures.
+// Returns 1 if operation completes successfully, 0 if not (returns 1 if
+// no GPT data are found on the disk).
+int BasicMBRData::BlankGPTData(void) {
+   int allOK = 1, err;
+   uint8_t blank[512];
+
+   memset(blank, 0, 512);
+   switch (CheckForGPT()) {
+      case -1:
+         allOK = 0;
+         break;
+      case 0:
+         break;
+      case 1:
+         if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
+            if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
+               allOK = 0;
+            myDisk->Close();
+         } else allOK = 0;
+         break;
+      case 2:
+         if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
+            if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
+               (myDisk->Write(blank, 512) == 512)))
+               allOK = 0;
+            myDisk->Close();
+         } else allOK = 0;
+         break;
+      case 3:
+         if ((myDisk != NULL) && (myDisk->OpenForWrite())) {
+            if (!((myDisk->Seek(1)) && (myDisk->Write(blank, 512) == 512)))
+               allOK = 0;
+            if (!((myDisk->Seek(myDisk->DiskSize(&err) - 1)) &&
+                (myDisk->Write(blank, 512) == 512)))
+                allOK = 0;
+            myDisk->Close();
+         } else allOK = 0;
+         break;
+      default:
+         break;
+   } // switch()
+   return allOK;
+} // BasicMBRData::BlankGPTData
+
+/*********************************************************************
+ *                                                                   *
+ * Functions that set or get disk metadata (CHS geometry, disk size, *
+ * etc.)                                                             *
+ *                                                                   *
+ *********************************************************************/
+
+// Read the CHS geometry using OS calls, or if that fails, set to
+// the most common value for big disks (255 heads, 63 sectors per
+// track, & however many cylinders that computes to).
+void BasicMBRData::ReadCHSGeom(void) {
+   int err;
+
+   numHeads = myDisk->GetNumHeads();
+   numSecspTrack = myDisk->GetNumSecsPerTrack();
+   diskSize = myDisk->DiskSize(&err);
+   blockSize = myDisk->GetBlockSize();
+   partitions[0].SetGeometry(numHeads, numSecspTrack, diskSize, blockSize);
+} // BasicMBRData::ReadCHSGeom()
+
+// Find the low and high used partition numbers (numbered from 0).
+// Return value is the number of partitions found. Note that the
+// *low and *high values are both set to 0 when no partitions
+// are found, as well as when a single partition in the first
+// position exists. Thus, the return value is the only way to
+// tell when no partitions exist.
+int BasicMBRData::GetPartRange(uint32_t *low, uint32_t *high) {
+   uint32_t i;
+   int numFound = 0;
+
+   *low = MAX_MBR_PARTS + 1; // code for "not found"
+   *high = 0;
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if (partitions[i].GetStartLBA() != UINT32_C(0)) { // it exists
+         *high = i; // since we're counting up, set the high value
+         // Set the low value only if it's not yet found...
+         if (*low == (MAX_MBR_PARTS + 1))
+            *low = i;
+         numFound++;
+      } // if
+   } // for
+
+   // Above will leave *low pointing to its "not found" value if no partitions
+   // are defined, so reset to 0 if this is the case....
+   if (*low == (MAX_MBR_PARTS + 1))
+      *low = 0;
+   return numFound;
+} // GPTData::GetPartRange()
+
+// Converts 64-bit LBA value to MBR-style CHS value. Returns 1 if conversion
+// was within the range that can be expressed by CHS (including 0, for an
+// empty partition), 0 if the value is outside that range, and -1 if chs is
+// invalid.
+int BasicMBRData::LBAtoCHS(uint64_t lba, uint8_t * chs) {
+   uint64_t cylinder, head, sector; // all numbered from 0
+   uint64_t remainder;
+   int retval = 1;
+   int done = 0;
+
+   if (chs != NULL) {
+      // Special case: In case of 0 LBA value, zero out CHS values....
+      if (lba == 0) {
+         chs[0] = chs[1] = chs[2] = UINT8_C(0);
+         done = 1;
+      } // if
+      // If LBA value is too large for CHS, max out CHS values....
+      if ((!done) && (lba >= ((uint64_t) numHeads * numSecspTrack * MAX_CYLINDERS))) {
+         chs[0] = 254;
+         chs[1] = chs[2] = 255;
+         done = 1;
+         retval = 0;
+      } // if
+      // If neither of the above applies, compute CHS values....
+      if (!done) {
+         cylinder = lba / (uint64_t) (numHeads * numSecspTrack);
+         remainder = lba - (cylinder * numHeads * numSecspTrack);
+         head = remainder / numSecspTrack;
+         remainder -= head * numSecspTrack;
+         sector = remainder;
+         if (head < numHeads)
+            chs[0] = (uint8_t) head;
+         else
+            retval = 0;
+         if (sector < numSecspTrack) {
+            chs[1] = (uint8_t) ((sector + 1) + (cylinder >> 8) * 64);
+            chs[2] = (uint8_t) (cylinder & UINT64_C(0xFF));
+         } else {
+            retval = 0;
+         } // if/else
+      } // if value is expressible and non-0
+   } else { // Invalid (NULL) chs pointer
+      retval = -1;
+   } // if CHS pointer valid
+   return (retval);
+} // BasicMBRData::LBAtoCHS()
+
+// Look for overlapping partitions. Also looks for a couple of non-error
+// conditions that the user should be told about.
+// Returns the number of problems found
+int BasicMBRData::FindOverlaps(void) {
+   int i, j, numProbs = 0, numEE = 0, ProtectiveOnOne = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      for (j = i + 1; j < MAX_MBR_PARTS; j++) {
+         if ((partitions[i].GetInclusion() != NONE) && (partitions[j].GetInclusion() != NONE) &&
+             (partitions[i].DoTheyOverlap(partitions[j]))) {
+            numProbs++;
+            cout << "\nProblem: MBR partitions " << i + 1 << " and " << j + 1
+                 << " overlap!\n";
+         } // if
+      } // for (j...)
+      if (partitions[i].GetType() == 0xEE) {
+         numEE++;
+         if (partitions[i].GetStartLBA() == 1)
+            ProtectiveOnOne = 1;
+      } // if
+   } // for (i...)
+
+   if (numEE > 1)
+      cout << "\nCaution: More than one 0xEE MBR partition found. This can cause problems\n"
+           << "in some OSes.\n";
+   if (!ProtectiveOnOne && (numEE > 0))
+      cout << "\nWarning: 0xEE partition doesn't start on sector 1. This can cause "
+           << "problems\nin some OSes.\n";
+
+   return numProbs;
+} // BasicMBRData::FindOverlaps()
+
+// Returns the number of primary partitions, including the extended partition
+// required to hold any logical partitions found.
+int BasicMBRData::NumPrimaries(void) {
+   int i, numPrimaries = 0, logicalsFound = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if (partitions[i].GetLengthLBA() > 0) {
+         if (partitions[i].GetInclusion() == PRIMARY)
+            numPrimaries++;
+         if (partitions[i].GetInclusion() == LOGICAL)
+            logicalsFound = 1;
+      } // if
+   } // for
+   return (numPrimaries + logicalsFound);
+} // BasicMBRData::NumPrimaries()
+
+// Returns the number of logical partitions.
+int BasicMBRData::NumLogicals(void) {
+   int i, numLogicals = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if (partitions[i].GetInclusion() == LOGICAL)
+         numLogicals++;
+   } // for
+   return numLogicals;
+} // BasicMBRData::NumLogicals()
+
+// Returns the number of partitions (primaries plus logicals), NOT including
+// the extended partition required to house the logicals.
+int BasicMBRData::CountParts(void) {
+   int i, num = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetInclusion() == LOGICAL) ||
+          (partitions[i].GetInclusion() == PRIMARY))
+         num++;
+   } // for
+   return num;
+} // BasicMBRData::CountParts()
+
+// Updates the canBeLogical and canBePrimary flags for all the partitions.
+void BasicMBRData::UpdateCanBeLogical(void) {
+   int i, j, sectorBefore, numPrimaries, numLogicals, usedAsEBR;
+   uint64_t firstLogical, lastLogical, lStart, pStart;
+
+   numPrimaries = NumPrimaries();
+   numLogicals = NumLogicals();
+   firstLogical = FirstLogicalLBA() - 1;
+   lastLogical = LastLogicalLBA();
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      usedAsEBR = (SectorUsedAs(partitions[i].GetLastLBA()) == EBR);
+      if (usedAsEBR) {
+         partitions[i].SetCanBeLogical(0);
+         partitions[i].SetCanBePrimary(0);
+      } else if (partitions[i].GetLengthLBA() > 0) {
+         // First determine if it can be logical....
+         sectorBefore = SectorUsedAs(partitions[i].GetStartLBA() - 1);
+         lStart = partitions[i].GetStartLBA(); // start of potential logical part.
+         if ((lastLogical > 0) &&
+             ((sectorBefore == EBR) || (sectorBefore == NONE))) {
+            // Assume it can be logical, then search for primaries that make it
+            // not work and, if found, flag appropriately.
+            partitions[i].SetCanBeLogical(1);
+            for (j = 0; j < MAX_MBR_PARTS; j++) {
+               if ((i != j) && (partitions[j].GetInclusion() == PRIMARY)) {
+                  pStart = partitions[j].GetStartLBA();
+                  if (((pStart < lStart) && (firstLogical < pStart)) ||
+                      ((pStart > lStart) && (firstLogical > pStart))) {
+                     partitions[i].SetCanBeLogical(0);
+                  } // if/else
+               } // if
+            } // for
+         } else {
+            if ((sectorBefore != EBR) && (sectorBefore != NONE))
+               partitions[i].SetCanBeLogical(0);
+            else
+               partitions[i].SetCanBeLogical(lastLogical == 0); // can be logical only if no logicals already
+         } // if/else
+         // Now determine if it can be primary. Start by assuming it can be...
+         partitions[i].SetCanBePrimary(1);
+         if ((numPrimaries >= 4) && (partitions[i].GetInclusion() != PRIMARY)) {
+            partitions[i].SetCanBePrimary(0);
+            if ((partitions[i].GetInclusion() == LOGICAL) && (numLogicals == 1) &&
+                (numPrimaries == 4))
+               partitions[i].SetCanBePrimary(1);
+         } // if
+         if ((partitions[i].GetStartLBA() > (firstLogical + 1)) &&
+             (partitions[i].GetLastLBA() < lastLogical))
+            partitions[i].SetCanBePrimary(0);
+      } // else if
+   } // for
+} // BasicMBRData::UpdateCanBeLogical()
+
+// Returns the first sector occupied by any logical partition. Note that
+// this does NOT include the logical partition's EBR! Returns UINT32_MAX
+// if there are no logical partitions defined.
+uint64_t BasicMBRData::FirstLogicalLBA(void) {
+   int i;
+   uint64_t firstFound = UINT32_MAX;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetInclusion() == LOGICAL) &&
+          (partitions[i].GetStartLBA() < firstFound)) {
+         firstFound = partitions[i].GetStartLBA();
+      } // if
+   } // for
+   return firstFound;
+} // BasicMBRData::FirstLogicalLBA()
+
+// Returns the last sector occupied by any logical partition, or 0 if
+// there are no logical partitions defined.
+uint64_t BasicMBRData::LastLogicalLBA(void) {
+   int i;
+   uint64_t lastFound = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetInclusion() == LOGICAL) &&
+          (partitions[i].GetLastLBA() > lastFound))
+         lastFound = partitions[i].GetLastLBA();
+   } // for
+   return lastFound;
+} // BasicMBRData::LastLogicalLBA()
+
+// Returns 1 if logical partitions are contiguous (have no primaries
+// in their midst), or 0 if one or more primaries exist between
+// logicals.
+int BasicMBRData::AreLogicalsContiguous(void) {
+   int allOK = 1, i = 0;
+   uint64_t firstLogical, lastLogical;
+
+   firstLogical = FirstLogicalLBA() - 1; // subtract 1 for EBR
+   lastLogical = LastLogicalLBA();
+   if (lastLogical > 0) {
+      do {
+         if ((partitions[i].GetInclusion() == PRIMARY) &&
+             (partitions[i].GetStartLBA() >= firstLogical) &&
+             (partitions[i].GetStartLBA() <= lastLogical)) {
+            allOK = 0;
+         } // if
+         i++;
+      } while ((i < MAX_MBR_PARTS) && allOK);
+   } // if
+   return allOK;
+} // BasicMBRData::AreLogicalsContiguous()
+
+// Returns 1 if all partitions fit on the disk, given its size; 0 if any
+// partition is too big.
+int BasicMBRData::DoTheyFit(void) {
+   int i, allOK = 1;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize)) {
+         allOK = 0;
+      } // if
+   } // for
+   return allOK;
+} // BasicMBRData::DoTheyFit(void)
+
+// Returns 1 if there's at least one free sector immediately preceding
+// all partitions flagged as logical; 0 if any logical partition lacks
+// this space.
+int BasicMBRData::SpaceBeforeAllLogicals(void) {
+   int i = 0, allOK = 1;
+
+   do {
+      if ((partitions[i].GetStartLBA() > 0) && (partitions[i].GetInclusion() == LOGICAL)) {
+         allOK = allOK && (SectorUsedAs(partitions[i].GetStartLBA() - 1) == EBR);
+      } // if
+      i++;
+   } while (allOK && (i < MAX_MBR_PARTS));
+   return allOK;
+} // BasicMBRData::SpaceBeforeAllLogicals()
+
+// Returns 1 if the partitions describe a legal layout -- all logicals
+// are contiguous and have at least one preceding empty sector,
+// the number of primaries is under 4 (or under 3 if there are any
+// logicals), there are no overlapping partitions, etc.
+// Does NOT assume that primaries are numbered 1-4; uses the
+// IsItPrimary() function of the MBRPart class to determine
+// primary status. Also does NOT consider partition order; there
+// can be gaps and it will still be considered legal.
+int BasicMBRData::IsLegal(void) {
+   int allOK = 1;
+
+   allOK = (FindOverlaps() == 0);
+   allOK = (allOK && (NumPrimaries() <= 4));
+   allOK = (allOK && AreLogicalsContiguous());
+   allOK = (allOK && DoTheyFit());
+   allOK = (allOK && SpaceBeforeAllLogicals());
+   return allOK;
+} // BasicMBRData::IsLegal()
+
+// Returns 1 if the 0xEE partition in the protective/hybrid MBR is marked as
+// active/bootable.
+int BasicMBRData::IsEEActive(void) {
+   int i, IsActive = 0;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetStatus() & 0x80) && (partitions[i].GetType() == 0xEE))
+         IsActive = 1;
+   }
+   return IsActive;
+} // BasicMBRData::IsEEActive()
+
+// Finds the next in-use partition, starting with start (will return start
+// if it's in use). Returns -1 if no subsequent partition is in use.
+int BasicMBRData::FindNextInUse(int start) {
+   if (start >= MAX_MBR_PARTS)
+      start = -1;
+   while ((start < MAX_MBR_PARTS) && (start >= 0) && (partitions[start].GetInclusion() == NONE))
+      start++;
+   if ((start < 0) || (start >= MAX_MBR_PARTS))
+      start = -1;
+   return start;
+} // BasicMBRData::FindFirstLogical();
+
+/*****************************************************
+ *                                                   *
+ * Functions to create, delete, or change partitions *
+ *                                                   *
+ *****************************************************/
+
+// Empty all data. Meant mainly for calling by constructors, but it's also
+// used by the hybrid MBR functions in the GPTData class.
+void BasicMBRData::EmptyMBR(int clearBootloader) {
+   int i;
+
+   // Zero out the boot loader section, the disk signature, and the
+   // 2-byte nulls area only if requested to do so. (This is the
+   // default.)
+   if (clearBootloader == 1) {
+      EmptyBootloader();
+   } // if
+
+   // Blank out the partitions
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      partitions[i].Empty();
+   } // for
+   MBRSignature = MBR_SIGNATURE;
+   state = mbr;
+} // BasicMBRData::EmptyMBR()
+
+// Blank out the boot loader area. Done with the initial MBR-to-GPT
+// conversion, since MBR boot loaders don't understand GPT, and so
+// need to be replaced....
+void BasicMBRData::EmptyBootloader(void) {
+   int i;
+
+   for (i = 0; i < 440; i++)
+      code[i] = 0;
+   nulls = 0;
+} // BasicMBRData::EmptyBootloader
+
+// Create a partition of the specified number based on the passed
+// partition. This function does *NO* error checking, so it's possible
+// to seriously screw up a partition table using this function!
+// Note: This function should NOT be used to create the 0xEE partition
+// in a conventional GPT configuration, since that partition has
+// specific size requirements that this function won't handle. It may
+// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
+// since those toss the rulebook away anyhow....
+void BasicMBRData::AddPart(int num, const MBRPart& newPart) {
+   partitions[num] = newPart;
+} // BasicMBRData::AddPart()
+
+// Create a partition of the specified number, starting LBA, and
+// length. This function does almost no error checking, so it's possible
+// to seriously screw up a partition table using this function!
+// Note: This function should NOT be used to create the 0xEE partition
+// in a conventional GPT configuration, since that partition has
+// specific size requirements that this function won't handle. It may
+// be used for creating the 0xEE partition(s) in a hybrid MBR, though,
+// since those toss the rulebook away anyhow....
+void BasicMBRData::MakePart(int num, uint64_t start, uint64_t length, int type, int bootable) {
+   if ((num >= 0) && (num < MAX_MBR_PARTS) && (start <= UINT32_MAX) && (length <= UINT32_MAX)) {
+      partitions[num].Empty();
+      partitions[num].SetType(type);
+      partitions[num].SetLocation(start, length);
+      if (num < 4)
+         partitions[num].SetInclusion(PRIMARY);
+      else
+         partitions[num].SetInclusion(LOGICAL);
+      SetPartBootable(num, bootable);
+   } // if valid partition number & size
+} // BasicMBRData::MakePart()
+
+// Set the partition's type code.
+// Returns 1 if successful, 0 if not (invalid partition number)
+int BasicMBRData::SetPartType(int num, int type) {
+   int allOK = 1;
+
+   if ((num >= 0) && (num < MAX_MBR_PARTS)) {
+      if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
+         allOK = partitions[num].SetType(type);
+      } else allOK = 0;
+   } else allOK = 0;
+   return allOK;
+} // BasicMBRData::SetPartType()
+
+// Set (or remove) the partition's bootable flag. Setting it is the
+// default; pass 0 as bootable to remove the flag.
+// Returns 1 if successful, 0 if not (invalid partition number)
+int BasicMBRData::SetPartBootable(int num, int bootable) {
+   int allOK = 1;
+
+   if ((num >= 0) && (num < MAX_MBR_PARTS)) {
+      if (partitions[num].GetLengthLBA() != UINT32_C(0)) {
+         if (bootable == 0)
+            partitions[num].SetStatus(UINT8_C(0x00));
+         else
+            partitions[num].SetStatus(UINT8_C(0x80));
+      } else allOK = 0;
+   } else allOK = 0;
+   return allOK;
+} // BasicMBRData::SetPartBootable()
+
+// Create a partition that fills the most available space. Returns
+// 1 if partition was created, 0 otherwise. Intended for use in
+// creating hybrid MBRs.
+int BasicMBRData::MakeBiggestPart(int i, int type) {
+   uint64_t start = UINT64_C(1); // starting point for each search
+   uint64_t firstBlock; // first block in a segment
+   uint64_t lastBlock; // last block in a segment
+   uint64_t segmentSize; // size of segment in blocks
+   uint64_t selectedSegment = UINT64_C(0); // location of largest segment
+   uint64_t selectedSize = UINT64_C(0); // size of largest segment in blocks
+   int found = 0;
+   string anything;
+
+   do {
+      firstBlock = FindFirstAvailable(start);
+      if (firstBlock > UINT64_C(0)) { // something's free...
+         lastBlock = FindLastInFree(firstBlock);
+         segmentSize = lastBlock - firstBlock + UINT64_C(1);
+         if (segmentSize > selectedSize) {
+            selectedSize = segmentSize;
+            selectedSegment = firstBlock;
+         } // if
+         start = lastBlock + 1;
+      } // if
+   } while (firstBlock != 0);
+   if ((selectedSize > UINT64_C(0)) && (selectedSize < diskSize)) {
+      found = 1;
+      MakePart(i, selectedSegment, selectedSize, type, 0);
+   } else {
+      found = 0;
+   } // if/else
+   return found;
+} // BasicMBRData::MakeBiggestPart(int i)
+
+// Delete partition #i
+void BasicMBRData::DeletePartition(int i) {
+   partitions[i].Empty();
+} // BasicMBRData::DeletePartition()
+
+// Set the inclusion status (PRIMARY, LOGICAL, or NONE) with some sanity
+// checks to ensure the table remains legal.
+// Returns 1 on success, 0 on failure.
+int BasicMBRData::SetInclusionwChecks(int num, int inclStatus) {
+   int allOK = 1, origValue;
+
+   if (IsLegal()) {
+      if ((inclStatus == PRIMARY) || (inclStatus == LOGICAL) || (inclStatus == NONE)) {
+         origValue = partitions[num].GetInclusion();
+         partitions[num].SetInclusion(inclStatus);
+         if (!IsLegal()) {
+            partitions[num].SetInclusion(origValue);
+            cerr << "Specified change is not legal! Aborting change!\n";
+         } // if
+      } else {
+         cerr << "Invalid partition inclusion code in BasicMBRData::SetInclusionwChecks()!\n";
+      } // if/else
+   } else {
+      cerr << "Partition table is not currently in a valid state. Aborting change!\n";
+      allOK = 0;
+   } // if/else
+   return allOK;
+} // BasicMBRData::SetInclusionwChecks()
+
+// Recomputes the CHS values for the specified partition and adjusts the value.
+// Note that this will create a technically incorrect CHS value for EFI GPT (0xEE)
+// protective partitions, but this is required by some buggy BIOSes, so I'm
+// providing a function to do this deliberately at the user's command.
+// This function does nothing if the partition's length is 0.
+void BasicMBRData::RecomputeCHS(int partNum) {
+   partitions[partNum].RecomputeCHS();
+} // BasicMBRData::RecomputeCHS()
+
+// Sorts the partitions starting with partition #start. This function
+// does NOT pay attention to primary/logical assignment, which is
+// critical when writing the partitions.
+void BasicMBRData::SortMBR(int start) {
+   if ((start < MAX_MBR_PARTS) && (start >= 0))
+      sort(partitions + start, partitions + MAX_MBR_PARTS);
+} // BasicMBRData::SortMBR()
+
+// Delete any partitions that are too big to fit on the disk
+// or that are too big for MBR (32-bit limits).
+// This deletes the partitions by setting values to 0, not just
+// by setting them as being omitted.
+// Returns the number of partitions deleted in this way.
+int BasicMBRData::DeleteOversizedParts() {
+   int num = 0, i;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetStartLBA() > diskSize) || (partitions[i].GetLastLBA() > diskSize) ||
+          (partitions[i].GetStartLBA() > UINT32_MAX) || (partitions[i].GetLengthLBA() > UINT32_MAX)) {
+         cerr << "\aWarning: Deleting oversized partition #" << i + 1 << "! Start = "
+              << partitions[i].GetStartLBA() << ", length = " << partitions[i].GetLengthLBA() << "\n";
+         partitions[i].Empty();
+         num++;
+      } // if
+   } // for
+   return num;
+} // BasicMBRData::DeleteOversizedParts()
+
+// Search for and delete extended partitions.
+// Returns the number of partitions deleted.
+int BasicMBRData::DeleteExtendedParts() {
+   int i, numDeleted = 0;
+   uint8_t type;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      type = partitions[i].GetType();
+      if (((type == 0x05) || (type == 0x0f) || (type == (0x85))) &&
+          (partitions[i].GetLengthLBA() > 0)) {
+         partitions[i].Empty();
+         numDeleted++;
+      } // if
+   } // for
+   return numDeleted;
+} // BasicMBRData::DeleteExtendedParts()
+
+// Finds any overlapping partitions and omits the smaller of the two.
+void BasicMBRData::OmitOverlaps() {
+   int i, j;
+
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      for (j = i + 1; j < MAX_MBR_PARTS; j++) {
+         if ((partitions[i].GetInclusion() != NONE) &&
+             partitions[i].DoTheyOverlap(partitions[j])) {
+            if (partitions[i].GetLengthLBA() < partitions[j].GetLengthLBA())
+               partitions[i].SetInclusion(NONE);
+            else
+               partitions[j].SetInclusion(NONE);
+         } // if
+      } // for (j...)
+   } // for (i...)
+} // BasicMBRData::OmitOverlaps()
+
+// Convert as many partitions into logicals as possible, except for
+// the first partition, if possible.
+void BasicMBRData::MaximizeLogicals() {
+   int earliestPart = 0, earliestPartWas = NONE, i;
+
+   for (i = MAX_MBR_PARTS - 1; i >= 0; i--) {
+      UpdateCanBeLogical();
+      earliestPart = i;
+      if (partitions[i].CanBeLogical()) {
+         partitions[i].SetInclusion(LOGICAL);
+      } else if (partitions[i].CanBePrimary()) {
+         partitions[i].SetInclusion(PRIMARY);
+      } else {
+         partitions[i].SetInclusion(NONE);
+      } // if/elseif/else
+   } // for
+   // If we have spare primaries, convert back the earliest partition to
+   // its original state....
+   if ((NumPrimaries() < 4) && (partitions[earliestPart].GetInclusion() == LOGICAL))
+      partitions[earliestPart].SetInclusion(earliestPartWas);
+} // BasicMBRData::MaximizeLogicals()
+
+// Add primaries up to the maximum allowed, from the omitted category.
+void BasicMBRData::MaximizePrimaries() {
+   int num, i = 0;
+
+   num = NumPrimaries();
+   while ((num < 4) && (i < MAX_MBR_PARTS)) {
+      if ((partitions[i].GetInclusion() == NONE) && (partitions[i].CanBePrimary())) {
+         partitions[i].SetInclusion(PRIMARY);
+         num++;
+         UpdateCanBeLogical();
+      } // if
+      i++;
+   } // while
+} // BasicMBRData::MaximizePrimaries()
+
+// Remove primary partitions in excess of 4, starting with the later ones,
+// in terms of the array location....
+void BasicMBRData::TrimPrimaries(void) {
+   int numToDelete, i = MAX_MBR_PARTS - 1;
+
+   numToDelete = NumPrimaries() - 4;
+   while ((numToDelete > 0) && (i >= 0)) {
+      if (partitions[i].GetInclusion() == PRIMARY) {
+         partitions[i].SetInclusion(NONE);
+         numToDelete--;
+      } // if
+      i--;
+   } // while (numToDelete > 0)
+} // BasicMBRData::TrimPrimaries()
+
+// Locates primary partitions located between logical partitions and
+// either converts the primaries into logicals (if possible) or omits
+// them.
+void BasicMBRData::MakeLogicalsContiguous(void) {
+   uint64_t firstLogicalLBA, lastLogicalLBA;
+   int i;
+
+   firstLogicalLBA = FirstLogicalLBA();
+   lastLogicalLBA = LastLogicalLBA();
+   for (i = 0; i < MAX_MBR_PARTS; i++) {
+      if ((partitions[i].GetInclusion() == PRIMARY) &&
+          (partitions[i].GetStartLBA() >= firstLogicalLBA) &&
+          (partitions[i].GetLastLBA() <= lastLogicalLBA)) {
+         if (SectorUsedAs(partitions[i].GetStartLBA() - 1) == NONE)
+            partitions[i].SetInclusion(LOGICAL);
+         else
+            partitions[i].SetInclusion(NONE);
+      } // if
+   } // for
+} // BasicMBRData::MakeLogicalsContiguous()
+
+// If MBR data aren't legal, adjust primary/logical assignments and,
+// if necessary, drop partitions, to make the data legal.
+void BasicMBRData::MakeItLegal(void) {
+   if (!IsLegal()) {
+      DeleteOversizedParts();
+      MaximizeLogicals();
+      MaximizePrimaries();
+      if (!AreLogicalsContiguous())
+         MakeLogicalsContiguous();
+      if (NumPrimaries() > 4)
+         TrimPrimaries();
+      OmitOverlaps();
+   } // if
+} // BasicMBRData::MakeItLegal()
+
+// Removes logical partitions and deactivated partitions from first four
+// entries (primary space).
+// Returns the number of partitions moved.
+int BasicMBRData::RemoveLogicalsFromFirstFour(void) {
+   int i, j = 4, numMoved = 0, swapped = 0;
+   MBRPart temp;
+
+   for (i = 0; i < 4; i++) {
+      if ((partitions[i].GetInclusion() != PRIMARY) && (partitions[i].GetLengthLBA() > 0)) {
+         j = 4;
+         swapped = 0;
+         do {
+            if ((partitions[j].GetInclusion() == NONE) && (partitions[j].GetLengthLBA() == 0)) {
+               temp = partitions[j];
+               partitions[j] = partitions[i];
+               partitions[i] = temp;
+               swapped = 1;
+               numMoved++;
+            } // if
+            j++;
+         } while ((j < MAX_MBR_PARTS) && !swapped);
+         if (j >= MAX_MBR_PARTS)
+            cerr << "Warning! Too many partitions in BasicMBRData::RemoveLogicalsFromFirstFour()!\n";
+      } // if
+   } // for i...
+   return numMoved;
+} // BasicMBRData::RemoveLogicalsFromFirstFour()
+
+// Move all primaries into the first four partition spaces
+// Returns the number of partitions moved.
+int BasicMBRData::MovePrimariesToFirstFour(void) {
+   int i, j = 0, numMoved = 0, swapped = 0;
+   MBRPart temp;
+
+   for (i = 4; i < MAX_MBR_PARTS; i++) {
+      if (partitions[i].GetInclusion() == PRIMARY) {
+         j = 0;
+         swapped = 0;
+         do {
+            if (partitions[j].GetInclusion() != PRIMARY) {
+               temp = partitions[j];
+               partitions[j] = partitions[i];
+               partitions[i] = temp;
+               swapped = 1;
+               numMoved++;
+            } // if
+            j++;
+         } while ((j < 4) && !swapped);
+      } // if
+   } // for
+   return numMoved;
+} // BasicMBRData::MovePrimariesToFirstFour()
+
+// Create an extended partition, if necessary, to hold the logical partitions.
+// This function also sorts the primaries into the first four positions of
+// the table.
+// Returns 1 on success, 0 on failure.
+int BasicMBRData::CreateExtended(void) {
+   int allOK = 1, i = 0, swapped = 0;
+   MBRPart temp;
+
+   if (IsLegal()) {
+      // Move logicals out of primary space...
+      RemoveLogicalsFromFirstFour();
+      // Move primaries out of logical space...
+      MovePrimariesToFirstFour();
+
+      // Create the extended partition
+      if (NumLogicals() > 0) {
+         SortMBR(4); // sort starting from 4 -- that is, logicals only
+         temp.Empty();
+         temp.SetStartLBA(FirstLogicalLBA() - 1);
+         temp.SetLengthLBA(LastLogicalLBA() - FirstLogicalLBA() + 2);
+         temp.SetType(0x0f, 1);
+         temp.SetInclusion(PRIMARY);
+         do {
+            if ((partitions[i].GetInclusion() == NONE) || (partitions[i].GetLengthLBA() == 0)) {
+               partitions[i] = temp;
+               swapped = 1;
+            } // if
+            i++;
+         } while ((i < 4) && !swapped);
+         if (!swapped) {
+            cerr << "Could not create extended partition; no room in primary table!\n";
+            allOK = 0;
+         } // if
+      } // if (NumLogicals() > 0)
+   } else allOK = 0;
+   // Do a final check for EFI GPT (0xEE) partitions & flag as a problem if found
+   // along with an extended partition
+   for (i = 0; i < MAX_MBR_PARTS; i++)
+      if (swapped && partitions[i].GetType() == 0xEE)
+         allOK = 0;
+   return allOK;
+} // BasicMBRData::CreateExtended()
+
+/****************************************
+ *                                      *
+ * Functions to find data on free space *
+ *                                      *
+ ****************************************/
+
+// Finds the first free space on the disk from start onward; returns 0
+// if none available....
+uint64_t BasicMBRData::FindFirstAvailable(uint64_t start) {
+   uint64_t first;
+   uint64_t i;
+   int firstMoved;
+
+   if ((start >= (UINT32_MAX - 1)) || (start >= (diskSize - 1)))
+      return 0;
+
+   first = start;
+
+   // ...now search through all partitions; if first is within an
+   // existing partition, move it to the next sector after that
+   // partition and repeat. If first was moved, set firstMoved
+   // flag; repeat until firstMoved is not set, so as to catch
+   // cases where partitions are out of sequential order....
+   do {
+      firstMoved = 0;
+      for (i = 0; i < 4; i++) {
+         // Check if it's in the existing partition
+         if ((first >= partitions[i].GetStartLBA()) &&
+             (first < (partitions[i].GetStartLBA() + partitions[i].GetLengthLBA()))) {
+            first = partitions[i].GetStartLBA() + partitions[i].GetLengthLBA();
+            firstMoved = 1;
+         } // if
+      } // for
+   } while (firstMoved == 1);
+   if ((first >= diskSize) || (first > UINT32_MAX))
+      first = 0;
+   return (first);
+} // BasicMBRData::FindFirstAvailable()
+
+// Finds the last free sector on the disk from start forward.
+uint64_t BasicMBRData::FindLastInFree(uint64_t start) {
+   uint64_t nearestStart;
+   uint64_t i;
+
+   if ((diskSize <= UINT32_MAX) && (diskSize > 0))
+      nearestStart = diskSize - 1;
+   else
+      nearestStart = UINT32_MAX - 1;
+
+   for (i = 0; i < 4; i++) {
+      if ((nearestStart > partitions[i].GetStartLBA()) &&
+          (partitions[i].GetStartLBA() > start)) {
+         nearestStart = partitions[i].GetStartLBA() - 1;
+      } // if
+   } // for
+   return (nearestStart);
+} // BasicMBRData::FindLastInFree()
+
+// Finds the first free sector on the disk from start backward.
+uint64_t BasicMBRData::FindFirstInFree(uint64_t start) {
+   uint64_t bestLastLBA, thisLastLBA;
+   int i;
+
+   bestLastLBA = 1;
+   for (i = 0; i < 4; i++) {
+      thisLastLBA = partitions[i].GetLastLBA() + 1;
+      if (thisLastLBA > 0)
+         thisLastLBA--;
+      if ((thisLastLBA > bestLastLBA) && (thisLastLBA < start))
+         bestLastLBA = thisLastLBA + 1;
+   } // for
+   return (bestLastLBA);
+} // BasicMBRData::FindFirstInFree()
+
+// Returns NONE (unused), PRIMARY, LOGICAL, EBR (for EBR or MBR), or INVALID.
+// Note: If the sector immediately before a logical partition is in use by
+// another partition, this function returns PRIMARY or LOGICAL for that
+// sector, rather than EBR.
+int BasicMBRData::SectorUsedAs(uint64_t sector, int topPartNum) {
+   int i = 0, usedAs = NONE;
+
+   do {
+      if ((partitions[i].GetStartLBA() <= sector) && (partitions[i].GetLastLBA() >= sector))
+         usedAs = partitions[i].GetInclusion();
+      if ((partitions[i].GetStartLBA() == (sector + 1)) && (partitions[i].GetInclusion() == LOGICAL))
+         usedAs = EBR;
+      if (sector == 0)
+         usedAs = EBR;
+      if (sector >= diskSize)
+         usedAs = INVALID;
+      i++;
+   } while ((i < topPartNum) && ((usedAs == NONE) || (usedAs == EBR)));
+   return usedAs;
+} // BasicMBRData::SectorUsedAs()
+
+/******************************************************
+ *                                                    *
+ * Functions that extract data on specific partitions *
+ *                                                    *
+ ******************************************************/
+
+uint8_t BasicMBRData::GetStatus(int i) {
+   MBRPart* thePart;
+   uint8_t retval;
+
+   thePart = GetPartition(i);
+   if (thePart != NULL)
+      retval = thePart->GetStatus();
+   else
+      retval = UINT8_C(0);
+   return retval;
+} // BasicMBRData::GetStatus()
+
+uint8_t BasicMBRData::GetType(int i) {
+   MBRPart* thePart;
+   uint8_t retval;
+
+   thePart = GetPartition(i);
+   if (thePart != NULL)
+      retval = thePart->GetType();
+   else
+      retval = UINT8_C(0);
+   return retval;
+} // BasicMBRData::GetType()
+
+uint64_t BasicMBRData::GetFirstSector(int i) {
+   MBRPart* thePart;
+   uint64_t retval;
+
+   thePart = GetPartition(i);
+   if (thePart != NULL) {
+      retval = thePart->GetStartLBA();
+   } else
+      retval = UINT32_C(0);
+      return retval;
+} // BasicMBRData::GetFirstSector()
+
+uint64_t BasicMBRData::GetLength(int i) {
+   MBRPart* thePart;
+   uint64_t retval;
+
+   thePart = GetPartition(i);
+   if (thePart != NULL) {
+      retval = thePart->GetLengthLBA();
+   } else
+      retval = UINT64_C(0);
+      return retval;
+} // BasicMBRData::GetLength()
+
+/***********************
+ *                     *
+ * Protected functions *
+ *                     *
+ ***********************/
+
+// Return a pointer to a primary or logical partition, or NULL if
+// the partition is out of range....
+MBRPart* BasicMBRData::GetPartition(int i) {
+   MBRPart* thePart = NULL;
+
+   if ((i >= 0) && (i < MAX_MBR_PARTS))
+      thePart = &partitions[i];
+   return thePart;
+} // GetPartition()
+
+/*******************************************
+ *                                         *
+ * Functions that involve user interaction *
+ *                                         *
+ *******************************************/
+
+// Present the MBR operations menu. Note that the 'w' option does not
+// immediately write data; that's handled by the calling function.
+// Returns the number of partitions defined on exit, or -1 if the
+// user selected the 'q' option. (Thus, the caller should save data
+// if the return value is >0, or possibly >=0 depending on intentions.)
+int BasicMBRData::DoMenu(const string& prompt) {
+   int goOn = 1, quitting = 0, retval, num, haveShownInfo = 0;
+   unsigned int hexCode;
+   string tempStr;
+
+   do {
+      cout << prompt;
+      switch (ReadString()[0]) {
+         case '\0':
+            goOn = cin.good();
+            break;
+         case 'a': case 'A':
+            num = GetNumber(1, MAX_MBR_PARTS, 1, "Toggle active flag for partition: ") - 1;
+            if (partitions[num].GetInclusion() != NONE)
+               partitions[num].SetStatus(partitions[num].GetStatus() ^ 0x80);
+            break;
+         case 'c': case 'C':
+            for (num = 0; num < MAX_MBR_PARTS; num++)
+               RecomputeCHS(num);
+            break;
+         case 'l': case 'L':
+            num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as logical: ") - 1;
+            SetInclusionwChecks(num, LOGICAL);
+            break;
+         case 'o': case 'O':
+            num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to omit: ") - 1;
+            SetInclusionwChecks(num, NONE);
+            break;
+         case 'p': case 'P':
+            if (!haveShownInfo) {
+               cout << "\n** NOTE: Partition numbers do NOT indicate final primary/logical "
+                    << "status,\n** unlike in most MBR partitioning tools!\n\a";
+               cout << "\n** Extended partitions are not displayed, but will be generated "
+                    << "as required.\n";
+               haveShownInfo = 1;
+            } // if
+            DisplayMBRData();
+            break;
+         case 'q': case 'Q':
+            cout << "This will abandon your changes. Are you sure? ";
+            if (GetYN() == 'Y') {
+               goOn = 0;
+               quitting = 1;
+            } // if
+            break;
+         case 'r': case 'R':
+            num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to set as primary: ") - 1;
+            SetInclusionwChecks(num, PRIMARY);
+            break;
+         case 's': case 'S':
+            SortMBR();
+            break;
+         case 't': case 'T':
+            num = GetNumber(1, MAX_MBR_PARTS, 1, "Partition to change type code: ") - 1;
+            hexCode = 0x00;
+            if (partitions[num].GetLengthLBA() > 0) {
+               while ((hexCode <= 0) || (hexCode > 255)) {
+                  cout << "Enter an MBR hex code: ";
+                  tempStr = ReadString();
+                  if (IsHex(tempStr))
+                     sscanf(tempStr.c_str(), "%x", &hexCode);
+               } // while
+               partitions[num].SetType(hexCode);
+            } // if
+            break;
+         case 'w': case 'W':
+            goOn = 0;
+            break;
+         default:
+            ShowCommands();
+            break;
+      } // switch
+   } while (goOn);
+   if (quitting)
+      retval = -1;
+   else
+      retval = CountParts();
+   return (retval);
+} // BasicMBRData::DoMenu()
+
+void BasicMBRData::ShowCommands(void) {
+   cout << "a\ttoggle the active/boot flag\n";
+   cout << "c\trecompute all CHS values\n";
+   cout << "l\tset partition as logical\n";
+   cout << "o\tomit partition\n";
+   cout << "p\tprint the MBR partition table\n";
+   cout << "q\tquit without saving changes\n";
+   cout << "r\tset partition as primary\n";
+   cout << "s\tsort MBR partitions\n";
+   cout << "t\tchange partition type code\n";
+   cout << "w\twrite the MBR partition table to disk and exit\n";
+} // BasicMBRData::ShowCommands()
diff --git a/gptfdisk/basicmbr.h b/gptfdisk/basicmbr.h
new file mode 100644
index 0000000..b809856
--- /dev/null
+++ b/gptfdisk/basicmbr.h
@@ -0,0 +1,152 @@
+/* basicmbr.h -- MBR data structure definitions, types, and functions */
+
+/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include "diskio.h"
+#include "mbrpart.h"
+
+#ifndef __BASICMBRSTRUCTS
+#define __BASICMBRSTRUCTS
+
+#define MBR_SIGNATURE UINT16_C(0xAA55)
+
+// Maximum number of MBR partitions
+#define MAX_MBR_PARTS 128
+
+using namespace std;
+
+/****************************************
+ *                                      *
+ * MBRData class and related structures *
+ *                                      *
+ ****************************************/
+
+// A 512-byte data structure into which the MBR can be loaded in one
+// go. Also used when loading logical partitions.
+#pragma pack(1)
+struct TempMBR {
+   uint8_t code[440];
+   uint32_t diskSignature;
+   uint16_t nulls;
+   struct MBRRecord partitions[4];
+   uint16_t MBRSignature;
+}; // struct TempMBR
+
+// Possible states of the MBR
+enum MBRValidity {invalid, gpt, hybrid, mbr};
+
+// Full data in tweaked MBR format
+class BasicMBRData {
+protected:
+   uint8_t code[440];
+   uint32_t diskSignature;
+   uint16_t nulls;
+   // MAX_MBR_PARTS defaults to 128. This array holds both the primary and
+   // the logical partitions, to simplify data retrieval for GPT conversions.
+   MBRPart partitions[MAX_MBR_PARTS];
+   uint16_t MBRSignature;
+
+   // Above are basic MBR data; now add more stuff....
+   uint32_t blockSize; // block size (usually 512)
+   uint64_t diskSize; // size in blocks
+   uint32_t numHeads; // number of heads, in CHS scheme
+   uint32_t numSecspTrack; // number of sectors per track, in CHS scheme
+   DiskIO* myDisk;
+   int canDeleteMyDisk;
+   string device;
+   MBRValidity state;
+   MBRPart* GetPartition(int i); // Return primary or logical partition
+public:
+   BasicMBRData(void);
+   BasicMBRData(string deviceFilename);
+   ~BasicMBRData(void);
+   BasicMBRData & operator=(const BasicMBRData & orig);
+
+   // File I/O functions...
+   int ReadMBRData(const string & deviceFilename);
+   int ReadMBRData(DiskIO * theDisk, int checkBlockSize = 1);
+   int ReadLogicalParts(uint64_t extendedStart, int partNum);
+   int WriteMBRData(void);
+   int WriteMBRData(DiskIO *theDisk);
+   int WriteMBRData(const string & deviceFilename);
+   int WriteMBRData(struct TempMBR & mbr, DiskIO *theDisk, uint64_t sector);
+   void DiskSync(void) {myDisk->DiskSync();}
+   void SetDisk(DiskIO *theDisk);
+
+   // Display data for user...
+   void DisplayMBRData(void);
+   void ShowState(void);
+
+   // GPT checks and fixes...
+   int CheckForGPT(void);
+   int BlankGPTData(void);
+
+   // Functions that set or get disk metadata (size, CHS geometry, etc.)
+   void SetDiskSize(uint64_t ds) {diskSize = ds;}
+   void SetBlockSize(uint32_t bs) {blockSize = bs;}
+   MBRValidity GetValidity(void) {return state;}
+   void SetHybrid(void) {state = hybrid;} // Set hybrid flag
+   void ReadCHSGeom(void);
+   int GetPartRange(uint32_t* low, uint32_t* high);
+   int LBAtoCHS(uint64_t lba, uint8_t * chs); // Convert LBA to CHS
+   int FindOverlaps(void);
+   int NumPrimaries(void);
+   int NumLogicals(void);
+   int CountParts(void);
+   void UpdateCanBeLogical(void);
+   uint64_t FirstLogicalLBA(void);
+   uint64_t LastLogicalLBA(void);
+   int AreLogicalsContiguous(void);
+   int DoTheyFit(void);
+   int SpaceBeforeAllLogicals(void);
+   int IsLegal(void);
+   int IsEEActive(void);
+   int FindNextInUse(int start);
+
+   // Functions to create, delete, or change partitions
+   // Pass EmptyMBR 1 to clear the boot loader code, 0 to leave it intact
+   void EmptyMBR(int clearBootloader = 1);
+   void EmptyBootloader(void);
+   void AddPart(int num, const MBRPart& newPart);
+   void MakePart(int num, uint64_t startLBA, uint64_t lengthLBA, int type = 0x07,
+                 int bootable = 0);
+   int SetPartType(int num, int type);
+   int SetPartBootable(int num, int bootable = 1);
+   int MakeBiggestPart(int i, int type); // Make partition filling most space
+   void DeletePartition(int i);
+   int SetInclusionwChecks(int num, int inclStatus);
+   void RecomputeCHS(int partNum);
+   void SortMBR(int start = 0);
+   int DeleteOversizedParts();
+   int DeleteExtendedParts();
+   void OmitOverlaps(void);
+   void MaximizeLogicals();
+   void MaximizePrimaries();
+   void TrimPrimaries();
+   void MakeLogicalsContiguous(void);
+   void MakeItLegal(void);
+   int RemoveLogicalsFromFirstFour(void);
+   int MovePrimariesToFirstFour(void);
+   int CreateExtended(void);
+
+   // Functions to find information on free space....
+   uint64_t FindFirstAvailable(uint64_t start = 1);
+   uint64_t FindLastInFree(uint64_t start);
+   uint64_t FindFirstInFree(uint64_t start);
+   int SectorUsedAs(uint64_t sector, int topPartNum = MAX_MBR_PARTS);
+
+   // Functions to extract data on specific partitions....
+   uint8_t GetStatus(int i);
+   uint8_t GetType(int i);
+   uint64_t GetFirstSector(int i);
+   uint64_t GetLength(int i);
+
+   // User interaction functions....
+   int DoMenu(const string& prompt = "\nMBR command (? for help): ");
+   void ShowCommands(void);
+}; // class BasicMBRData
+
+#endif
diff --git a/gptfdisk/bsd.cc b/gptfdisk/bsd.cc
new file mode 100644
index 0000000..59557c2
--- /dev/null
+++ b/gptfdisk/bsd.cc
@@ -0,0 +1,334 @@
+/* bsd.cc -- Functions for loading and manipulating legacy BSD disklabel
+   data. */
+
+/* By Rod Smith, initial coding August, 2009 */
+
+/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <stdio.h>
+//#include <unistd.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <iostream>
+#include <string>
+#include "support.h"
+#include "bsd.h"
+
+using namespace std;
+
+
+BSDData::BSDData(void) {
+   state = unknown;
+   signature = UINT32_C(0);
+   signature2 = UINT32_C(0);
+   sectorSize = 512;
+   numParts = 0;
+   labelFirstLBA = 0;
+   labelLastLBA = 0;
+   labelStart = LABEL_OFFSET1; // assume raw disk format
+   partitions = NULL;
+} // default constructor
+
+BSDData::~BSDData(void) {
+   delete[] partitions;
+} // destructor
+
+// Read BSD disklabel data from the specified device filename. This function
+// just opens the device file and then calls an overloaded function to do
+// the bulk of the work. Returns 1 on success, 0 on failure.
+int BSDData::ReadBSDData(const string & device, uint64_t startSector, uint64_t endSector) {
+   int allOK = 1;
+   DiskIO myDisk;
+
+   if (device != "") {
+      if (myDisk.OpenForRead(device)) {
+         allOK = ReadBSDData(&myDisk, startSector, endSector);
+      } else {
+         allOK = 0;
+      } // if/else
+
+      myDisk.Close();
+   } else {
+      allOK = 0;
+   } // if/else
+   return allOK;
+} // BSDData::ReadBSDData() (device filename version)
+
+// Load the BSD disklabel data from an already-opened disk
+// file, starting with the specified sector number.
+int BSDData::ReadBSDData(DiskIO *theDisk, uint64_t startSector, uint64_t endSector) {
+   int allOK = 1;
+   int i, foundSig = 0, bigEnd = 0;
+   int relative = 0; // assume absolute partition sector numbering
+   uint8_t buffer[4096]; // I/O buffer
+   uint32_t realSig;
+   uint32_t* temp32;
+   uint16_t* temp16;
+   BSDRecord* tempRecords;
+   int offset[NUM_OFFSETS] = { LABEL_OFFSET1, LABEL_OFFSET2 };
+
+   labelFirstLBA = startSector;
+   labelLastLBA = endSector;
+   offset[1] = theDisk->GetBlockSize();
+
+   // Read 4096 bytes (eight 512-byte sectors or equivalent)
+   // into memory; we'll extract data from this buffer.
+   // (Done to work around FreeBSD limitation on size of reads
+   // from block devices.)
+   allOK = theDisk->Seek(startSector);
+   if (allOK) allOK = theDisk->Read(buffer, 4096);
+
+   // Do some strangeness to support big-endian architectures...
+   bigEnd = (IsLittleEndian() == 0);
+   realSig = BSD_SIGNATURE;
+   if (bigEnd && allOK)
+      ReverseBytes(&realSig, 4);
+
+   // Look for the signature at any of two locations.
+   // Note that the signature is repeated at both the original
+   // offset and 132 bytes later, so we need two checks....
+   if (allOK) {
+      i = 0;
+      do {
+         temp32 = (uint32_t*) &buffer[offset[i]];
+         signature = *temp32;
+         if (signature == realSig) { // found first, look for second
+            temp32 = (uint32_t*) &buffer[offset[i] + 132];
+            signature2 = *temp32;
+            if (signature2 == realSig) {
+               foundSig = 1;
+               labelStart = offset[i];
+            } // if found signature
+         } // if/else
+         i++;
+      } while ((!foundSig) && (i < NUM_OFFSETS));
+      allOK = foundSig;
+   } // if
+
+   // Load partition metadata from the buffer....
+   if (allOK) {
+      temp32 = (uint32_t*) &buffer[labelStart + 40];
+      sectorSize = *temp32;
+      temp16 = (uint16_t*) &buffer[labelStart + 138];
+      numParts = *temp16;
+   } // if
+
+   // Make it big-endian-aware....
+   if ((IsLittleEndian() == 0) && allOK)
+      ReverseMetaBytes();
+
+   // Check validity of the data and flag it appropriately....
+   if (foundSig && (numParts <= MAX_BSD_PARTS) && allOK) {
+      state = bsd;
+   } else {
+      state = bsd_invalid;
+   } // if/else
+
+   // If the state is good, go ahead and load the main partition data....
+   if (state == bsd) {
+      partitions = new struct BSDRecord[numParts * sizeof(struct BSDRecord)];
+      if (partitions == NULL) {
+         cerr << "Unable to allocate memory in BSDData::ReadBSDData()! Terminating!\n";
+         exit(1);
+      } // if
+      for (i = 0; i < numParts; i++) {
+         // Once again, we use the buffer, but index it using a BSDRecord
+         // pointer (dangerous, but effective)....
+         tempRecords = (BSDRecord*) &buffer[labelStart + 148];
+         partitions[i].lengthLBA = tempRecords[i].lengthLBA;
+         partitions[i].firstLBA = tempRecords[i].firstLBA;
+         partitions[i].fsType = tempRecords[i].fsType;
+         if (bigEnd) { // reverse data (fsType is a single byte)
+            ReverseBytes(&partitions[i].lengthLBA, 4);
+            ReverseBytes(&partitions[i].firstLBA, 4);
+         } // if big-endian
+         // Check for signs of relative sector numbering: A "0" first sector
+         // number on a partition with a non-zero length -- but ONLY if the
+         // length is less than the disk size, since NetBSD has a habit of
+         // creating a disk-sized partition within a carrier MBR partition
+         // that's too small to house it, and this throws off everything....
+         if ((partitions[i].firstLBA == 0) && (partitions[i].lengthLBA > 0)
+             && (partitions[i].lengthLBA < labelLastLBA))
+            relative = 1;
+      } // for
+      // Some disklabels use sector numbers relative to the enclosing partition's
+      // start, others use absolute sector numbers. If relative numbering was
+      // detected above, apply a correction to all partition start sectors....
+      if (relative) {
+         for (i = 0; i < numParts; i++) {
+            partitions[i].firstLBA += (uint32_t) startSector;
+         } // for
+      } // if
+   } // if signatures OK
+//   DisplayBSDData();
+   return allOK;
+} // BSDData::ReadBSDData(DiskIO* theDisk, uint64_t startSector)
+
+// Reverse metadata's byte order; called only on big-endian systems
+void BSDData::ReverseMetaBytes(void) {
+   ReverseBytes(&signature, 4);
+   ReverseBytes(&sectorSize, 4);
+   ReverseBytes(&signature2, 4);
+   ReverseBytes(&numParts, 2);
+} // BSDData::ReverseMetaByteOrder()
+
+// Display basic BSD partition data. Used for debugging.
+void BSDData::DisplayBSDData(void) {
+   int i;
+
+   if (state == bsd) {
+      cout << "BSD partitions:\n";
+      for (i = 0; i < numParts; i++) {
+         cout.width(4);
+         cout << i + 1 << "\t";
+         cout.width(13);
+         cout << partitions[i].firstLBA << "\t";
+         cout.width(15);
+         cout << partitions[i].lengthLBA << " \t0x";
+         cout.width(2);
+         cout.fill('0');
+         cout.setf(ios::uppercase);
+         cout << hex << (int) partitions[i].fsType << "\n" << dec;
+         cout.fill(' ');
+      } // for
+   } // if
+} // BSDData::DisplayBSDData()
+
+// Displays the BSD disklabel state. Called during program launch to inform
+// the user about the partition table(s) status
+int BSDData::ShowState(void) {
+   int retval = 0;
+
+   switch (state) {
+      case bsd_invalid:
+         cout << "  BSD: not present\n";
+         break;
+      case bsd:
+         cout << "  BSD: present\n";
+         retval = 1;
+         break;
+      default:
+         cout << "\a  BSD: unknown -- bug!\n";
+         break;
+   } // switch
+   return retval;
+} // BSDData::ShowState()
+
+// Weirdly, this function has stopped working when defined inline,
+// but it's OK here....
+int BSDData::IsDisklabel(void) {
+   return (state == bsd);
+} // BSDData::IsDiskLabel()
+
+// Returns the BSD table's partition type code
+uint8_t BSDData::GetType(int i) {
+   uint8_t retval = 0; // 0 = "unused"
+
+   if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
+      retval = partitions[i].fsType;
+
+   return(retval);
+} // BSDData::GetType()
+
+// Returns the number of the first sector of the specified partition
+uint64_t BSDData::GetFirstSector(int i) {
+   uint64_t retval = UINT64_C(0);
+
+   if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
+      retval = (uint64_t) partitions[i].firstLBA;
+
+   return retval;
+} // BSDData::GetFirstSector
+
+// Returns the length (in sectors) of the specified partition
+uint64_t BSDData::GetLength(int i) {
+   uint64_t retval = UINT64_C(0);
+
+   if ((i < numParts) && (i >= 0) && (state == bsd) && (partitions != 0))
+      retval = (uint64_t) partitions[i].lengthLBA;
+
+   return retval;
+} // BSDData::GetLength()
+
+// Returns the number of partitions defined in the current table
+int BSDData::GetNumParts(void) {
+   return numParts;
+} // BSDData::GetNumParts()
+
+// Returns the specified partition as a GPT partition. Used in BSD-to-GPT
+// conversion process
+GPTPart BSDData::AsGPT(int i) {
+   GPTPart guid;                  // dump data in here, then return it
+   uint64_t sectorOne, sectorEnd; // first & last sectors of partition
+   int passItOn = 1;              // Set to 0 if partition is empty or invalid
+
+   guid.BlankPartition();
+   sectorOne = (uint64_t) partitions[i].firstLBA;
+   sectorEnd = sectorOne + (uint64_t) partitions[i].lengthLBA;
+   if (sectorEnd > 0) sectorEnd--;
+   // Note on above: BSD partitions sometimes have a length of 0 and a start
+   // sector of 0. With unsigned ints, the usual way (start + length - 1) to
+   // find the end will result in a huge number, which will be confusing.
+   // Thus, apply the "-1" part only if it's reasonable to do so.
+
+   // Do a few sanity checks on the partition before we pass it on....
+   // First, check that it falls within the bounds of its container
+   // and that it starts before it ends....
+   if ((sectorOne < labelFirstLBA) || (sectorEnd > labelLastLBA) || (sectorOne > sectorEnd))
+      passItOn = 0;
+   // Some disklabels include a pseudo-partition that's the size of the entire
+   // disk or containing partition. Don't return it.
+   if ((sectorOne <= labelFirstLBA) && (sectorEnd >= labelLastLBA) &&
+       (GetType(i) == 0))
+      passItOn = 0;
+   // If the end point is 0, it's not a valid partition.
+   if ((sectorEnd == 0) || (sectorEnd == labelFirstLBA))
+      passItOn = 0;
+
+   if (passItOn) {
+      guid.SetFirstLBA(sectorOne);
+      guid.SetLastLBA(sectorEnd);
+      // Now set a random unique GUID for the partition....
+      guid.RandomizeUniqueGUID();
+      // ... zero out the attributes and name fields....
+      guid.SetAttributes(UINT64_C(0));
+      // Most BSD disklabel type codes seem to be archaic or rare.
+      // They're also ambiguous; a FreeBSD filesystem is impossible
+      // to distinguish from a NetBSD one. Thus, these code assignment
+      // are going to be rough to begin with. For a list of meanings,
+      // see http://fxr.watson.org/fxr/source/sys/dtype.h?v=DFBSD,
+      // or Google it.
+      switch (GetType(i)) {
+         case 1: // BSD swap
+            guid.SetType(0xa502); break;
+         case 7: // BSD FFS
+            guid.SetType(0xa503); break;
+         case 8: case 11: // MS-DOS or HPFS
+            guid.SetType(0x0700); break;
+         case 9: // log-structured fs
+            guid.SetType(0xa903); break;
+         case 13: // bootstrap
+            guid.SetType(0xa501); break;
+         case 14: // vinum
+            guid.SetType(0xa505); break;
+         case 15: // RAID
+            guid.SetType(0xa903); break;
+         case 27: // FreeBSD ZFS
+            guid.SetType(0xa504); break;
+         default:
+            guid.SetType(0xa503); break;
+      } // switch
+      // Set the partition name to the name of the type code....
+      guid.SetName(guid.GetTypeName());
+   } // if
+   return guid;
+} // BSDData::AsGPT()
diff --git a/gptfdisk/bsd.h b/gptfdisk/bsd.h
new file mode 100644
index 0000000..ffbe5cc
--- /dev/null
+++ b/gptfdisk/bsd.h
@@ -0,0 +1,93 @@
+/* bsd.h -- BSD disklabel data structure definitions, types, and functions */
+
+/* This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include "gptpart.h"
+#include "diskio.h"
+
+#ifndef __BSD_STRUCTS
+#define __BSD_STRUCTS
+
+#define BSD_SIGNATURE UINT32_C(0x82564557)  /* BSD disklabel signature ("magic") */
+
+// BSD disklabels can start at offsets of 64 or the sector size -- at least,
+// I *THINK* that's what's going on. I've seen them at 64 or 512 on disks
+// with 512-byte blocks and at 2048 on disks with 2048-byte blocks. The
+// LABEL_OFFSET2 value will be replaced by the block size in the
+// ReadBSDData() function....
+#define LABEL_OFFSET1 64
+#define LABEL_OFFSET2 512
+#define NUM_OFFSETS 2
+
+// FreeBSD documents a maximum # of partitions of 8, but I saw 16 on a NetBSD
+// disk. I'm quadrupling that for further safety. Note that BSDReadData()
+// uses a 4096-byte I/O buffer. In combination with LABEL_OFFSET3 and the
+// additional 148-byte offset to the actual partition data, that gives a
+// theoretical maximum of 118.75 partitions that the program can handle before
+// memory errors will occur.
+#define MAX_BSD_PARTS 64
+
+
+using namespace std;
+
+/****************************************
+ *                                      *
+ * BSDData class and related structures *
+ *                                      *
+ ****************************************/
+
+// Possible states of the MBR
+enum BSDValidity {unknown, bsd_invalid, bsd};
+
+// Data for a single BSD partition record
+// Create entries for all fields, although we only use lengthLBA, firstLBA,
+// and fsType, to simplify loading the data from disk....
+struct  BSDRecord {      // the partition table
+   uint32_t lengthLBA;   // number of sectors in partition
+   uint32_t firstLBA;    // starting sector
+   uint32_t fragSize;    // filesystem basic fragment size
+   uint8_t  fsType;      // filesystem type, see below
+   uint8_t  frag;        // filesystem fragments per block
+   uint16_t pcpg;        // filesystem cylinders per group
+};
+
+// Full data in tweaked BSD format
+// For some reason this has to be packed or MS Visual C++'s debugger complains
+// about memory errors whenever a BSDData variable is destroyed.
+#pragma pack (8)
+class BSDData {
+   protected:
+      // We only need a few items from the main BSD disklabel data structure....
+      uint32_t signature;        // the magic number
+      uint32_t sectorSize;       // # of bytes per sector
+      uint32_t signature2;       // the magic number (again)
+      uint16_t numParts;         // number of partitions in table
+      struct BSDRecord* partitions;     // partition array
+
+      // Above are basic BSD disklabel data; now add more stuff....
+      uint64_t labelFirstLBA;    // first sector of BSD disklabel (partition or disk)
+      uint64_t labelLastLBA;     // final sector of BSD disklabel
+      uint64_t labelStart;       // BSD disklabel start point in bytes from labelFirstLBA
+      BSDValidity state;
+   public:
+      BSDData(void);
+      ~BSDData(void);
+      int ReadBSDData(const string & deviceFilename, uint64_t startSector, uint64_t endSector);
+      int ReadBSDData(DiskIO *myDisk, uint64_t startSector, uint64_t endSector);
+      void ReverseMetaBytes(void);
+      void DisplayBSDData(void);
+      int ShowState(void); // returns 1 if BSD disklabel detected
+      int IsDisklabel(void);
+
+      // Functions to extract data on specific partitions....
+      uint8_t GetType(int i);
+      uint64_t GetFirstSector(int i);
+      uint64_t GetLength(int i);
+      int GetNumParts(void);
+      GPTPart AsGPT(int i); // Return BSD part. as GPT part.
+}; // struct MBRData
+
+#endif
diff --git a/gptfdisk/cgdisk.8 b/gptfdisk/cgdisk.8
new file mode 100644
index 0000000..ac3da9b
--- /dev/null
+++ b/gptfdisk/cgdisk.8
@@ -0,0 +1,391 @@
+.\" Copyright 2011-2015 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" May be distributed under the GNU General Public License
+.TH "CGDISK" "8" "1.0.1" "Roderick W. Smith" "GPT fdisk Manual"
+.SH "NAME"
+cgdisk \- Curses-based GUID partition table (GPT) manipulator
+.SH "SYNOPSIS"
+.BI "cgdisk "
+[ \-a ]
+.I device
+
+.SH "DESCRIPTION"
+
+GPT fdisk is a text\-mode family of programs for creation and manipulation
+of partition tables. The \fBcgdisk\fR member of this family employs a
+curses-based user interface for interaction using a text\-mode menuing
+system. It will automatically convert an old\-style Master Boot Record
+(MBR) partition table or BSD disklabel stored without an MBR carrier
+partition to the newer Globally Unique Identifier (GUID) Partition Table
+(GPT) format, or will load a GUID partition table. Other members of this
+program family are \fBgdisk\fR (the most feature-rich program of the group,
+with a non-curses-based interactive user interface) and \fBsgdisk\fR (which
+is driven via command-line options for use by experts or in scripts).
+FixParts is a related program for fixing a limited set of problems with MBR
+disks.
+
+For information on MBR vs. GPT, as well as GPT terminology and structure,
+see the extended GPT fdisk documentation at
+\fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia.
+
+The \fBcgdisk\fR program employs a user interface similar to that of Linux's
+\fBcfdisk\fR, but \fBcgdisk\fR modifies GPT partitions. It also has the
+capability of transforming MBR partitions or BSD disklabels into GPT
+partitions. Like the original \fBcfdisk\fR program, \fBcgdisk\fR does not
+modify disk structures until you explicitly write them to disk, so if you
+make a mistake, you can exit from the program with the Quit option to leave
+your partitions unmodified.
+
+Ordinarily, \fBcgdisk\fR operates on disk device files, such as
+\fI/dev/sda\fR or \fI/dev/hda\fR under Linux, \fI/dev/disk0\fR under
+Mac OS X, or \fI/dev/ad0\fR or \fI/dev/da0\fR under FreeBSD. The program
+can also operate on disk image files, which can be either copies of whole
+disks (made with \fBdd\fR, for instance) or raw disk images used by
+emulators such as QEMU or VMWare. Note that only \fIraw\fR disk images
+are supported; \fBcgdisk\fR cannot work on compressed or other advanced
+disk image formats.
+
+Upon start, \fBcgdisk\fR attempts to identify the partition type in use on
+the disk. If it finds valid GPT data, \fBcgdisk\fR will use it. If
+\fBcgdisk\fR finds a valid MBR or BSD disklabel but no GPT data, it will
+attempt to convert the MBR or disklabel into GPT form. (BSD disklabels are
+likely to have unusable first and/or final partitions because they overlap
+with the GPT data structures, though.) Upon exiting with the 'w' option,
+\fBcgdisk\fR replaces the MBR or disklabel with a GPT. \fIThis action is
+potentially dangerous!\fR Your system may become unbootable, and partition
+type codes may become corrupted if the disk uses unrecognized type codes.
+Boot problems are particularly likely if you're multi\-booting with any
+GPT\-unaware OS. If you mistakenly launch \fBcgdisk\fR on an MBR disk, you
+can safely exit the program without making any changes by using the Quit
+option.
+
+When creating a fresh partition table, certain considerations may be in
+order:
+
+.TP 
+.B *
+For data (non\-boot) disks, and for boot disks used on BIOS\-based computers
+with GRUB as the boot loader, partitions may be created in whatever order
+and in whatever sizes are desired.
+
+.TP 
+.B *
+Boot disks for EFI\-based systems require an \fIEFI System
+Partition\fR (GPT fdisk internal code 0xEF00) formatted as FAT\-32.
+The recommended size of this partition is between 100 and 300 MiB.
+Boot\-related files are stored here. (Note that GNU Parted identifies
+such partitions as having the "boot flag" set.)
+
+.TP 
+.B *
+The GRUB 2 boot loader for BIOS\-based systems makes use of a \fIBIOS Boot
+Partition\fR (GPT fdisk internal code 0xEF02), in which the secondary
+boot loader is stored, without the benefit of a filesystem. This partition
+can typically be quite small (roughly 32 KiB to 1 MiB), but you should
+consult your boot loader documentation for details.
+
+.TP 
+.B *
+If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft
+Reserved\fR (GPT fdisk
+internal code 0x0C01) is recommended. This partition should be about 128 MiB
+in size. It ordinarily follows the EFI System Partition and immediately
+precedes the Windows data partitions. (Note that old versions of GNU Parted
+create all FAT partitions as this type, which actually makes the partition
+unusable for normal file storage in both Windows and Mac OS X.)
+
+.TP 
+.B *
+Some OSes' GPT utilities create some blank space (typically 128 MiB) after
+each partition. The intent is to enable future disk utilities to use this
+space. Such free space is not required of GPT disks, but creating it may
+help in future disk maintenance. You can use GPT fdisk's relative partition
+positioning option (specifying the starting sector as '+128M', for
+instance) to simplify creating such gaps.
+
+.SH "OPTIONS"
+
+.PP 
+
+Only one command-line option is accepted, aside from the device filename:
+\fI\-a\fR. This option alters the highlighting of partitions and blocks of
+free space: Instead of using ncurses, when \fI\-a\fR is used \fBcgdisk\fR
+uses a ">" symbol to the left of the selected partition or free space.
+This option is intended for use on limited display devices such as
+teletypes and screen readers.
+
+Interactions with \fBcgdisk\fR occur with its interactive text\-mode menus.
+The display is broken into two interactive parts:
+
+.TP
+.B *
+The partition display area, in which partitions and gaps between them
+(marked as "free space") are summarized.
+
+.TP
+.B *
+The option selection area, in which buttons for the main options appear.
+
+.PP
+
+In addition, the top of the display shows the program's name and version
+number, the device filename associated with the disk, and the disk's size
+in both sectors and IEEE-1541 units (GiB, TiB, and so on).
+
+You can use the following keys to move among the various options and to
+select among them:
+
+.TP
+.B up arrow
+This key moves the partition selection up by one partition.
+
+.TP
+.B down arrow
+This key moves the partition selection down by one partition.
+
+.TP
+.B Page Up
+This key moves the partition selection up by one screen.
+
+.TP
+.B Page Down
+This key moves the partition selection down by one screen.
+
+.TP
+.B right arrow
+This key moves the option selection to the right by one item.
+
+.TP
+.B left arrow
+This key moves the option selection to the left by one item.
+
+.TP
+.B Enter
+This key activates the currently selected option. You can also activate an
+option by typing the capitalized letter in the option's name on the
+keyboard, such as \fBa\fR to activate the Align option.
+
+.PP
+
+If more partitions exist than can be displayed in one screen, you can
+scroll between screens using the partition selection keys, much as in a
+text editor.
+
+Available options are as described below. (Note that \fBcgdisk\fR provides
+a much more limited set of options than its sibling \fBgdisk\fR. If you
+need to perform partition table recovery, hybrid MBR modifcation, or other
+advanced operations, you should consult the \fBgdisk\fR documentation.)
+
+.TP 
+.B Align
+Change the sector alignment value. Disks with more logical sectors than
+physical sectors (such as modern Advanced Format drives), some RAID
+configurations, and many SSD devices, can suffer performance problems if
+partitions are not aligned properly for their internal data structures. On
+new disks, GPT fdisk attempts to align partitions on 1MiB boundaries
+(2048\-sectors on disks with 512-byte sectors) by default, which optimizes
+performance for all of these disk types. On pre\-partitioned disks, GPT
+fdisk attempts to identify the alignment value used on that disk, but will
+set 8-sector alignment on disks larger than 300 GB even if lesser alignment
+values are detected. In either case, it can be changed by using this
+option.
+
+.TP 
+.B Backup
+Save partition data to a backup file. You can back up your current
+in\-memory partition table to a disk file using this option. The resulting
+file is a binary file consisting of the protective MBR, the main GPT
+header, the backup GPT header, and one copy of the partition table, in that
+order. Note that the backup is of the current in\-memory data structures, so
+if you launch the program, make changes, and then use this option, the
+backup will reflect your changes.
+
+.TP 
+.B Delete
+Delete a partition. This action deletes the entry from the partition table
+but does not disturb the data within the sectors originally allocated to
+the partition on the disk. If a corresponding hybrid MBR partition exists,
+\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT)
+MBR protective partition to fill the new free space.
+
+.TP 
+.B Help
+Print brief descriptions of all the options.
+
+.TP 
+.B Info
+Show detailed partition information. The summary information shown in the
+partition display area necessarily omits many details, such as the
+partitions' unique GUIDs and the partitions' sector-exact start and end
+points. The Info option displays this information for a single partition.
+
+.TP 
+.B Load
+Load partition data from a backup file. This option is the reverse of the
+Backup option. Note that restoring partition data from anything but the
+original disk is not recommended.
+
+.TP 
+.B naMe
+Change the GPT name of a partition. This name is encoded as a UTF\-16
+string, but proper entry and display of anything beyond basic ASCII values
+requires suitable locale and font support. For the most part, Linux ignores
+the partition name, but it may be important in some OSes. GPT fdisk sets a
+default name based on the partition type code. Note that the GPT partition
+name is different from the filesystem name, which is encoded in the
+filesystem's data structures. Note also that to activate this item by
+typing its alphabetic equivalent, you must use \fBM\fR, not the more
+obvious \fBN\fR, because the latter is used by the next option....
+
+.TP 
+.B New
+Create a new partition. You enter a starting sector, a size, a type code,
+and a name. The start sector can be specified in absolute terms as a sector
+number or as a position measured in kibibytes (K), mebibytes (M), gibibytes
+(G), tebibytes (T), or pebibytes (P); for instance, \fI\fB40M\fR\fR
+specifies a position 40MiB from the start of the disk. You can specify
+locations relative to the start or end of the specified default range by
+preceding the number by a '+' symbol, as in \fI\fB+2G\fR\fR to specify a
+point 2GiB after the default start sector. The size value can use the K, M,
+G, T, and P suffixes, too. Pressing the Enter key with no input specifies
+the default value, which is the start of the largest available block for
+the start sector and the full available size for the size.
+
+.TP 
+.B Quit
+Quit from the program \fIwithout saving your changes\fR.
+Use this option if you just wanted to view information or if you make a
+mistake and want to back out of all your changes.
+
+.TP 
+.B Type
+Change a single partition's type code. You enter the type code using a
+two\-byte hexadecimal number. You may also enter a GUID directly, if you
+have one and \fBcgdisk\fR doesn't know it. If you don't know the type code
+for your partition, you can type \fBL\fR to see a list of known type codes.
+
+.TP 
+.B Verify
+Verify disk. This option checks for a variety of problems, such as
+incorrect CRCs and mismatched main and backup data. This option does not
+automatically correct most problems, though; for that, you must use
+\fBgdisk\fR. If no problems are found, this command displays a summary of
+unallocated disk space.
+
+.TP 
+.B Write
+Write data. Use this command to save your changes.
+
+.SH "BUGS"
+
+Known bugs and limitations include:
+
+.TP 
+.B *
+The program compiles correctly only on Linux, FreeBSD, and Mac OS X. In
+theory, it should compile under Windows if the Ncurses library for Windows
+is installed, but I have not tested this capability. 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. Under FreeBSD,
+32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit
+versions for Mac OS X has been tested by the author.
+
+.TP 
+.B *
+The FreeBSD version of the program can't write changes to the partition
+table to a disk when existing partitions on that disk are mounted. (The
+same problem exists with many other FreeBSD utilities, such as
+\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome
+by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt.
+
+.TP
+.B *
+The program can load only up to 128 partitions (4 primary partitions and
+124 logical partitions) when converting from MBR format. This limit can
+be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the
+\fIbasicmbr.h\fR source code file and recompiling; however, such a change
+will require using a larger\-than\-normal partition table. (The limit
+of 128 partitions was chosen because that number equals the 128 partitions
+supported by the most common partition table size.)
+
+.TP 
+.B *
+Converting from MBR format sometimes fails because of insufficient space at
+the start or (more commonly) the end of the disk. Resizing the partition
+table (using the 's' option in the experts' menu in \fBgdisk\fR) can
+sometimes overcome this problem; however, in extreme cases it may be
+necessary to resize a partition using GNU Parted or a similar tool prior to
+conversion with GPT fdisk.
+
+.TP 
+.B *
+MBR conversions work only if the disk has correct LBA partition
+descriptors. These descriptors should be present on any disk over 8 GiB in
+size or on smaller disks partitioned with any but very ancient software.
+
+.TP 
+.B *
+BSD disklabel support can create first and/or last partitions that overlap
+with the GPT data structures. This can sometimes be compensated by
+adjusting the partition table size, but in extreme cases the affected
+partition(s) may need to be deleted.
+
+.TP 
+.B *
+Because of the highly variable nature of BSD disklabel structures,
+conversions from this form may be unreliable \-\- partitions may be dropped,
+converted in a way that creates overlaps with other partitions, or
+converted with incorrect start or end values. Use this feature with
+caution!
+
+.TP 
+.B *
+Booting after converting an MBR or BSD disklabel disk is likely to be
+disrupted. Sometimes re\-installing a boot loader will fix the problem, but
+other times you may need to switch boot loaders. Except on EFI\-based
+platforms, Windows through at least Windows 7 doesn't support booting from
+GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery &
+transformation menu in \fBgdisk\fR) or abandoning GPT in favor of MBR may
+be your only options in this case.
+
+.TP
+.B *
+The \fBcgdisk\fR Verify function and the partition type listing obtainable
+by typing \fIL\fR in the Type function (or when specifying a partition type
+while creating a new partition) both currently exit ncurses mode. This
+limitation is a minor cosmetic blemish that does not affect functionality.
+
+.SH "AUTHORS"
+Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
+
+Contributors:
+
+* Yves Blusseau (1otnwmz02@sneakemail.com)
+
+* David Hubbard (david.c.hubbard@gmail.com)
+
+* Justin Maggard (justin.maggard@netgear.com)
+
+* Dwight Schauer (dschauer@gmail.com)
+
+* Florian Zumbiehl (florz@florz.de)
+
+
+.SH "SEE ALSO"
+\fBcfdisk (8)\fR,
+\fBfdisk (8)\fR,
+\fBgdisk (8)\fR,
+\fBmkfs (8)\fR,
+\fBparted (8)\fR,
+\fBsfdisk (8)\fR
+\fBsgdisk (8)\fR
+\fBfixparts (8)\fR
+
+\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
+
+\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR
+
+\fIhttp://www.rodsbooks.com/gdisk/\fR
+
+.SH "AVAILABILITY"
+The \fBcgdisk\fR command is part of the \fIGPT fdisk\fR package and is
+available from Rod Smith.
diff --git a/gptfdisk/cgdisk.cc b/gptfdisk/cgdisk.cc
new file mode 100644
index 0000000..f21f046
--- /dev/null
+++ b/gptfdisk/cgdisk.cc
@@ -0,0 +1,79 @@
+/*
+    Copyright (C) 2011  <Roderick W. Smith>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+
+/* This class implements an interactive curses-based interface atop the
+   GPTData class */
+
+#include <string.h>
+#include "gptcurses.h"
+
+using namespace std;
+
+#define MAX_OPTIONS 50
+
+int main(int argc, char *argv[]) {
+   string device = "";
+   int displayType = USE_CURSES;
+
+   if (!SizesOK())
+      exit(1);
+
+   switch (argc) {
+      case 1:
+         cout << "Type device filename, or press <Enter> to exit: ";
+         device = ReadString();
+         if (device.length() == 0)
+            exit(0);
+         break;
+      case 2: // basic usage
+         device = (string) argv[1];
+         break;
+      case 3: // "-a" usage or illegal
+         if (strcmp(argv[1], "-a") == 0) {
+            device = (string) argv[2];
+         } else if (strcmp(argv[2], "-a") == 0) {
+            device = (string) argv[1];
+         } else {
+            cerr << "Usage: " << argv[0] << " [-a] device_file\n";
+            exit(1);
+         } // if/elseif/else
+         displayType = USE_ARROW;
+         break;
+      default:
+         cerr << "Usage: " << argv[0] << " [-a] device_file\n";
+         exit(1);
+         break;
+   } // switch
+
+   GPTDataCurses theGPT;
+
+   theGPT.SetDisplayType(displayType);
+   if (theGPT.LoadPartitions(device)) {
+      if (theGPT.GetState() != use_gpt) {
+         Report("Warning! Non-GPT or damaged disk detected! This program will attempt to\n"
+                "convert to GPT form or repair damage to GPT data structures, but may not\n"
+                "succeed. Use gdisk or another disk repair tool if you have a damaged GPT\n"
+                "disk.");
+      } // if
+      theGPT.MainMenu();
+   } else {
+      Report("Could not load partitions from '" + device + "'! Aborting!");
+   } // if/else
+   return 0;
+} // main
diff --git a/gptfdisk/crc32.cc b/gptfdisk/crc32.cc
new file mode 100644
index 0000000..5eca100
--- /dev/null
+++ b/gptfdisk/crc32.cc
@@ -0,0 +1,72 @@
+/*

+ * efone - Distributed internet phone system.

+ *

+ * (c) 1999,2000 Krzysztof Dabrowski

+ * (c) 1999,2000 ElysiuM deeZine

+ *

+ * This program is free software; you can redistribute it and/or

+ * modify it under the terms of the GNU General Public License

+ * as published by the Free Software Foundation; either version

+ * 2 of the License, or (at your option) any later version.

+ *

+ */

+

+/* based on implementation by Finn Yannick Jacobs */

+

+#include <stdio.h>

+#include <stdlib.h>

+#include <sys/types.h>

+#include "crc32.h"

+

+/* crc_tab[] -- this crcTable is being build by chksum_crc32GenTab().

+ *		so make sure, you call it before using the other

+ *		functions!

+ */

+uint32_t crc_tab[256];

+

+/* chksum_crc() -- to a given block, this one calculates the

+ *				crc32-checksum until the length is

+ *				reached. the crc32-checksum will be

+ *				the result.

+ */

+uint32_t chksum_crc32 (unsigned char *block, unsigned int length)

+{

+   unsigned long crc;

+   unsigned long i;

+

+   crc = 0xFFFFFFFF;

+   for (i = 0; i < length; i++)

+   {

+      crc = ((crc >> 8) & 0x00FFFFFF) ^ crc_tab[(crc ^ *block++) & 0xFF];

+   }

+   return (crc ^ 0xFFFFFFFF);

+}

+

+/* chksum_crc32gentab() --      to a global crc_tab[256], this one will

+ *				calculate the crcTable for crc32-checksums.

+ *				it is generated to the polynom [..]

+ */

+

+void chksum_crc32gentab ()

+{

+   unsigned long crc, poly;

+   int i, j;

+

+   poly = 0xEDB88320L;

+   for (i = 0; i < 256; i++)

+   {

+      crc = i;

+      for (j = 8; j > 0; j--)

+      {

+	 if (crc & 1)

+	 {

+	    crc = (crc >> 1) ^ poly;

+	 }

+	 else

+	 {

+	    crc >>= 1;

+	 }

+      }

+      crc_tab[i] = crc;

+   }

+}

diff --git a/gptfdisk/crc32.h b/gptfdisk/crc32.h
new file mode 100644
index 0000000..b1ca28b
--- /dev/null
+++ b/gptfdisk/crc32.h
@@ -0,0 +1,20 @@
+/*

+ * efone - Distributed internet phone system.

+ *

+ * (c) 1999,2000 Krzysztof Dabrowski

+ * (c) 1999,2000 ElysiuM deeZine

+ *

+ * This program is free software; you can redistribute it and/or

+ * modify it under the terms of the GNU General Public License

+ * as published by the Free Software Foundation; either version

+ * 2 of the License, or (at your option) any later version.

+ *

+ */

+

+/* based on implementation by Finn Yannick Jacobs. */

+

+#include <stdint.h>

+

+void chksum_crc32gentab ();

+uint32_t chksum_crc32 (unsigned char *block, unsigned int length);

+extern unsigned int crc_tab[256];

diff --git a/gptfdisk/current.spec b/gptfdisk/current.spec
new file mode 100644
index 0000000..1587732
--- /dev/null
+++ b/gptfdisk/current.spec
@@ -0,0 +1,85 @@
+Summary: GPT partitioning and MBR repair software
+Name: gptfdisk
+Version: 1.0.1
+
+Release: 1%{?dist}
+License: GPLv2
+URL: http://www.rodsbooks.com/gdisk
+Group: Applications/System
+Source: http://www.rodsbooks.com/gdisk/gptfdisk-1.0.1.tar.gz
+BuildRoot: %(mktemp -ud %{_tmppath}/%{name}-%{version}-%{release}-XXXXXX)
+
+%description
+
+Partitioning software for GPT disks and to repair MBR disks. The gdisk,
+cgdisk, and sgdisk utilities (in the gdisk package) are GPT-enabled
+partitioning tools; the fixparts utility (in the fixparts package) fixes
+some problems with MBR disks that can be created by buggy partitioning
+software.
+
+%package -n gdisk
+
+Group: Applications/System
+
+Summary: An fdisk-like partitioning tool for GPT disks
+
+%description -n gdisk
+An fdisk-like partitioning tool for GPT disks. GPT
+fdisk features a command-line interface, fairly direct
+manipulation of partition table structures, recovery
+tools to help you deal with corrupt partition tables,
+and the ability to convert MBR disks to GPT format.
+
+%prep
+%setup -q
+
+%build
+CFLAGS="$RPM_OPT_FLAGS" CXXFLAGS="$RPM_OPT_CXX_FLAGS" make
+
+%install
+rm -rf $RPM_BUILD_ROOT
+mkdir -p $RPM_BUILD_ROOT/usr/sbin
+install -Dp -m0755 gdisk $RPM_BUILD_ROOT/usr/sbin
+install -Dp -m0755 sgdisk $RPM_BUILD_ROOT/usr/sbin
+install -Dp -m0755 cgdisk $RPM_BUILD_ROOT/usr/sbin
+install -Dp -m0755 fixparts $RPM_BUILD_ROOT/usr/sbin
+install -Dp -m0644 gdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/gdisk.8
+install -Dp -m0644 sgdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/sgdisk.8
+install -Dp -m0644 cgdisk.8 $RPM_BUILD_ROOT/%{_mandir}/man8/cgdisk.8
+install -Dp -m0644 fixparts.8 $RPM_BUILD_ROOT/%{_mandir}/man8/fixparts.8
+
+%clean
+rm -rf $RPM_BUILD_ROOT
+
+%files -n gdisk
+%defattr(-,root,root -)
+%doc NEWS COPYING README
+/usr/sbin/gdisk
+/usr/sbin/sgdisk
+/usr/sbin/cgdisk
+%doc %{_mandir}/man8/gdisk.8*
+%doc %{_mandir}/man8/sgdisk.8*
+%doc %{_mandir}/man8/cgdisk.8*
+
+%package -n fixparts
+
+Group: Applications/System
+
+Summary: A tool for repairing certain types of damage to MBR disks
+
+%description -n fixparts
+A program that corrects errors that can creep into MBR-partitioned
+disks. Removes stray GPT data, fixes mis-sized extended partitions,
+and enables changing primary vs. logical partition status. Also
+provides a few additional partition manipulation features.
+
+%files -n fixparts
+%defattr(-,root,root -)
+%doc NEWS COPYING README
+/usr/sbin/fixparts
+%doc %{_mandir}/man8/fixparts.8*
+
+
+%changelog
+* Sun Mar 2 2014 R Smith <rodsmith@rodsbooks.com> - 1.0.1
+- Created spec file for 1.0.1 release
diff --git a/gptfdisk/diskio-unix.cc b/gptfdisk/diskio-unix.cc
new file mode 100644
index 0000000..75fad2e
--- /dev/null
+++ b/gptfdisk/diskio-unix.cc
@@ -0,0 +1,465 @@
+//
+// C++ Interface: diskio (Unix components [Linux, FreeBSD, Mac OS X])
+//
+// Description: Class to handle low-level disk I/O for GPT fdisk
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <sys/ioctl.h>
+#include <string.h>
+#include <string>
+#include <stdint.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#ifdef __linux__
+#include "linux/hdreg.h"
+#endif
+
+#include <iostream>
+
+#include "diskio.h"
+
+using namespace std;
+
+// Returns the official "real" name for a shortened version of same.
+// Trivial here; more important in Windows
+void DiskIO::MakeRealName(void) {
+   realFilename = userFilename;
+} // DiskIO::MakeRealName()
+
+// 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;
+   struct stat64 st;
+
+   if (isOpen) { // file is already open
+      if (openForWrite) {
+         Close();
+      } else {
+         shouldOpen = 0;
+      } // if/else
+   } // if
+
+   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 << "You must run this program as root or use sudo!\n";
+         if (errno == ENOENT)
+            cerr << "The specified file does not exist!\n";
+         realFilename = "";
+         userFilename = "";
+         isOpen = 0;
+         openForWrite = 0;
+      } else {
+         isOpen = 0;
+         openForWrite = 0;
+         if (fstat64(fd, &st) == 0) {
+            if (S_ISDIR(st.st_mode))
+               cerr << "The specified path is a directory!\n";
+#if !(defined(__FreeBSD__) || defined(__FreeBSD_kernel__)) \
+                       && !defined(__APPLE__)
+            else if (S_ISCHR(st.st_mode))
+               cerr << "The specified path is a character device!\n";
+#endif
+            else if (S_ISFIFO(st.st_mode))
+               cerr << "The specified path is a FIFO!\n";
+            else if (S_ISSOCK(st.st_mode))
+               cerr << "The specified path is a socket!\n";
+            else
+               isOpen = 1;
+         } // if (fstat64()...)
+      } // if/else
+   } // if
+
+   return isOpen;
+} // DiskIO::OpenForRead(void)
+
+// An extended file-open function. This includes some system-specific checks.
+// Returns 1 if the file is open, 0 otherwise....
+int DiskIO::OpenForWrite(void) {
+   if ((isOpen) && (openForWrite))
+      return 1;
+
+   // Close the disk, in case it's already open for reading only....
+   Close();
+
+   // try to open the device; may fail....
+   fd = open(realFilename.c_str(), O_WRONLY | O_CREAT, S_IWUSR | S_IRUSR | S_IRGRP | S_IROTH);
+#ifdef __APPLE__
+   // MacOS X requires a shared lock under some circumstances....
+   if (fd < 0) {
+      cerr << "Warning: Devices opened with shared lock will not have their\npartition table automatically reloaded!\n";
+      fd = open(realFilename.c_str(), O_WRONLY | O_SHLOCK);
+   } // if
+#endif
+   if (fd >= 0) {
+      isOpen = 1;
+      openForWrite = 1;
+   } else {
+      isOpen = 0;
+      openForWrite = 0;
+   } // if/else
+   return isOpen;
+} // DiskIO::OpenForWrite(void)
+
+// Close the disk device. Note that this does NOT erase the stored filenames,
+// so the file can be re-opened without specifying the filename.
+void DiskIO::Close(void) {
+   if (isOpen)
+      if (close(fd) < 0)
+         cerr << "Warning! Problem closing file!\n";
+   isOpen = 0;
+   openForWrite = 0;
+} // 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). If the disk can't be opened at all, return a value of 0.
+int DiskIO::GetBlockSize(void) {
+   int err = -1, blockSize = 0;
+#ifdef __sun__
+   struct dk_minfo minfo;
+#endif
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+#ifdef __APPLE__
+      err = ioctl(fd, DKIOCGETBLOCKSIZE, &blockSize);
+#endif
+#ifdef __sun__
+      err = ioctl(fd, DKIOCGMEDIAINFO, &minfo);
+      if (err == 0)
+          blockSize = minfo.dki_lbsize;
+#endif
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+      err = ioctl(fd, DIOCGSECTORSIZE, &blockSize);
+#endif
+#ifdef __linux__
+      err = ioctl(fd, BLKSSZGET, &blockSize);
+#endif
+
+      if (err == -1) {
+         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 != 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)
+
+   return (blockSize);
+} // DiskIO::GetBlockSize()
+
+// Returns the number of heads, according to the kernel, or 255 if the
+// correct value can't be determined.
+uint32_t DiskIO::GetNumHeads(void) {
+   uint32_t numHeads = 255;
+
+#ifdef HDIO_GETGEO
+   struct hd_geometry geometry;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen)
+      OpenForRead();
+
+   if (!ioctl(fd, HDIO_GETGEO, &geometry))
+      numHeads = (uint32_t) geometry.heads;
+#endif
+   return numHeads;
+} // DiskIO::GetNumHeads();
+
+// Returns the number of sectors per track, according to the kernel, or 63
+// if the correct value can't be determined.
+uint32_t DiskIO::GetNumSecsPerTrack(void) {
+   uint32_t numSecs = 63;
+
+   #ifdef HDIO_GETGEO
+   struct hd_geometry geometry;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen)
+      OpenForRead();
+
+   if (!ioctl(fd, HDIO_GETGEO, &geometry))
+      numSecs = (uint32_t) geometry.sectors;
+   #endif
+   return numSecs;
+} // DiskIO::GetNumSecsPerTrack()
+
+// Resync disk caches so the OS uses the new partition table. This code varies
+// a lot from one OS to another.
+// Returns 1 on success, 0 if the kernel continues to use the old partition table.
+// (Note that for most OSes, the default of 0 is returned because I've not yet
+// looked into how to test for success in the underlying system calls...)
+int DiskIO::DiskSync(void) {
+   int i, retval = 0, platformFound = 0;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      sync();
+#if defined(__APPLE__) || defined(__sun__)
+      cout << "Warning: The kernel may continue to use old or deleted partitions.\n"
+           << "You should reboot or remove the drive.\n";
+               /* don't know if this helps
+               * it definitely will get things on disk though:
+               * http://topiks.org/mac-os-x/0321278542/ch12lev1sec8.html */
+#ifdef __sun__
+      i = ioctl(fd, DKIOCFLUSHWRITECACHE);
+#else
+      i = ioctl(fd, DKIOCSYNCHRONIZECACHE);
+#endif
+      platformFound++;
+#endif
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+      sleep(2);
+      i = ioctl(fd, DIOCGFLUSH);
+      cout << "Warning: The kernel may continue to use old or deleted partitions.\n"
+           << "You should reboot or remove the drive.\n";
+      platformFound++;
+#endif
+#ifdef __linux__
+      sleep(1); // Theoretically unnecessary, but ioctl() fails sometimes if omitted....
+      fsync(fd);
+      i = ioctl(fd, BLKRRPART);
+      if (i) {
+         cout << "Warning: The kernel is still using the old partition table.\n"
+              << "The new table will be used at the next reboot or after you\n"
+              << "run partprobe(8) or kpartx(8)\n";
+      } else {
+         retval = 1;
+      } // if/else
+      platformFound++;
+#endif
+      if (platformFound == 0)
+         cerr << "Warning: Platform not recognized!\n";
+      if (platformFound > 1)
+         cerr << "\nWarning: We seem to be running on multiple platforms!\n";
+   } // if (isOpen)
+   return retval;
+} // DiskIO::DiskSync()
+
+// Seek to the specified sector. Returns 1 on success, 0 on failure.
+// Note that seeking beyond the end of the file is NOT detected as a failure!
+int DiskIO::Seek(uint64_t sector) {
+   int retval = 1;
+   off_t seekTo, sought;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      retval = OpenForRead();
+   } // if
+
+   if (isOpen) {
+      seekTo = sector * (uint64_t) GetBlockSize();
+      sought = lseek64(fd, seekTo, SEEK_SET);
+      if (sought != seekTo) {
+         retval = 0;
+      } // if
+   } // if
+   return retval;
+} // DiskIO::Seek()
+
+// A variant on the standard read() function. Done to work around
+// limitations in FreeBSD concerning the matching of the sector
+// size with the number of bytes read.
+// Returns the number of bytes read into buffer.
+int DiskIO::Read(void* buffer, int numBytes) {
+   int blockSize, numBlocks, retval = 0;
+   char* tempSpace;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      // Compute required space and allocate memory
+      blockSize = GetBlockSize();
+      if (numBytes <= blockSize) {
+         numBlocks = 1;
+         tempSpace = new char [blockSize];
+      } else {
+         numBlocks = numBytes / blockSize;
+         if ((numBytes % blockSize) != 0)
+            numBlocks++;
+         tempSpace = new char [numBlocks * blockSize];
+      } // if/else
+      if (tempSpace == NULL) {
+         cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n";
+         exit(1);
+      } // if
+
+      // Read the data into temporary space, then copy it to buffer
+      retval = read(fd, tempSpace, numBlocks * blockSize);
+      memcpy(buffer, tempSpace, numBytes);
+
+      // Adjust the return value, if necessary....
+      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
+         retval = numBytes;
+
+      delete[] tempSpace;
+   } // if (isOpen)
+   return retval;
+} // DiskIO::Read()
+
+// A variant on the standard write() function. Done to work around
+// limitations in FreeBSD concerning the matching of the sector
+// size with the number of bytes read.
+// Returns the number of bytes written.
+int DiskIO::Write(void* buffer, int numBytes) {
+   int blockSize = 512, i, numBlocks, retval = 0;
+   char* tempSpace;
+
+   // If disk isn't open, try to open it....
+   if ((!isOpen) || (!openForWrite)) {
+      OpenForWrite();
+   } // if
+
+   if (isOpen) {
+      // Compute required space and allocate memory
+      blockSize = GetBlockSize();
+      if (numBytes <= blockSize) {
+         numBlocks = 1;
+         tempSpace = new char [blockSize];
+      } else {
+         numBlocks = numBytes / blockSize;
+         if ((numBytes % blockSize) != 0) numBlocks++;
+         tempSpace = new char [numBlocks * blockSize];
+      } // if/else
+      if (tempSpace == NULL) {
+         cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n";
+         exit(1);
+      } // if
+      
+      // Copy the data to my own buffer, then write it
+      memcpy(tempSpace, buffer, numBytes);
+      for (i = numBytes; i < numBlocks * blockSize; i++) {
+         tempSpace[i] = 0;
+      } // for
+      retval = write(fd, tempSpace, numBlocks * blockSize);
+
+      // Adjust the return value, if necessary....
+      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
+         retval = numBytes;
+
+      delete[] tempSpace;
+   } // if (isOpen)
+   return retval;
+} // DiskIO:Write()
+
+/**************************************************************************************
+ *                                                                                    *
+ * Below functions are lifted from various sources, as documented in comments before  *
+ * each one.                                                                          *
+ *                                                                                    *
+ **************************************************************************************/
+
+// The disksize function is taken from the Linux fdisk code and modified
+// greatly since then to enable FreeBSD and MacOS support, as well as to
+// return correct values for disk image files.
+uint64_t DiskIO::DiskSize(int *err) {
+   uint64_t sectors = 0; // size in sectors
+   off_t bytes = 0; // size in bytes
+   struct stat64 st;
+   int platformFound = 0;
+#ifdef __sun__
+   struct dk_minfo minfo;
+#endif
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      // Note to self: I recall testing a simplified version of
+      // this code, similar to what's in the __APPLE__ block,
+      // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
+      // systems but not on 64-bit. Keep this in mind in case of
+      // 32/64-bit issues on MacOS....
+#ifdef __APPLE__
+      *err = ioctl(fd, DKIOCGETBLOCKCOUNT, &sectors);
+      platformFound++;
+#endif
+#ifdef __sun__
+      *err = ioctl(fd, DKIOCGMEDIAINFO, &minfo);
+      if (*err == 0)
+          sectors = minfo.dki_capacity;
+      platformFound++;
+#endif
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__)
+      *err = ioctl(fd, DIOCGMEDIASIZE, &bytes);
+      long long b = GetBlockSize();
+      sectors = bytes / b;
+      platformFound++;
+#endif
+#ifdef __linux__
+      long sz;
+      long long b;
+      *err = ioctl(fd, BLKGETSIZE, &sz);
+      if (*err) {
+         sectors = sz = 0;
+      } // if
+      if ((!*err) || (errno == EFBIG)) {
+         *err = ioctl(fd, BLKGETSIZE64, &b);
+         if (*err || b == 0 || b == sz)
+            sectors = sz;
+         else
+            sectors = (b >> 9);
+      } // if
+      // Unintuitively, the above returns values in 512-byte blocks, no
+      // matter what the underlying device's block size. Correct for this....
+      sectors /= (GetBlockSize() / 512);
+      platformFound++;
+#endif
+      if (platformFound != 1)
+         cerr << "Warning! We seem to be running on no known platform!\n";
+
+      // The above methods have failed, so let's assume it's a regular
+      // file (a QEMU image, dd backup, or what have you) and see what
+      // fstat() gives us....
+      if ((sectors == 0) || (*err == -1)) {
+         if (fstat64(fd, &st) == 0) {
+            bytes = st.st_size;
+            if ((bytes % UINT64_C(512)) != 0)
+               cerr << "Warning: File size is not a multiple of 512 bytes!"
+                    << " Misbehavior is likely!\n\a";
+            sectors = bytes / UINT64_C(512);
+         } // if
+      } // if
+   } // if (isOpen)
+   return sectors;
+} // DiskIO::DiskSize()
diff --git a/gptfdisk/diskio-windows.cc b/gptfdisk/diskio-windows.cc
new file mode 100644
index 0000000..5535f49
--- /dev/null
+++ b/gptfdisk/diskio-windows.cc
@@ -0,0 +1,336 @@
+//
+// C++ Interface: diskio (Windows-specific components)
+//
+// Description: Class to handle low-level disk I/O for GPT fdisk
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// 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.
+
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+
+#include <windows.h>
+#include <winioctl.h>
+#define fstat64 fstat
+#define stat64 stat
+#define S_IRGRP 0
+#define S_IROTH 0
+#include <stdio.h>
+#include <string>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <iostream>
+
+#include "support.h"
+#include "diskio.h"
+
+using namespace std;
+
+// Returns the official Windows name for a shortened version of same.
+void DiskIO::MakeRealName(void) {
+   size_t colonPos;
+
+   colonPos = userFilename.find(':', 0);
+   if ((colonPos != string::npos) && (colonPos <= 3)) {
+      realFilename = "\\\\.\\physicaldrive";
+      realFilename += userFilename.substr(0, colonPos);
+   } else {
+      realFilename = userFilename;
+   } // if/else
+} // DiskIO::MakeRealName()
+
+// Open the currently on-record file for reading
+int DiskIO::OpenForRead(void) {
+   int shouldOpen = 1;
+
+   if (isOpen) { // file is already open
+      if (openForWrite) {
+         Close();
+      } else {
+         shouldOpen = 0;
+      } // if/else
+   } // if
+
+   if (shouldOpen) {
+      fd = CreateFile(realFilename.c_str(),GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE,
+                      NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+      if (fd == INVALID_HANDLE_VALUE) {
+         CloseHandle(fd);
+         cerr << "Problem opening " << realFilename << " for reading!\n";
+         realFilename = "";
+         userFilename = "";
+         isOpen = 0;
+         openForWrite = 0;
+      } else {
+         isOpen = 1;
+         openForWrite = 0;
+      } // if/else
+   } // if
+
+   return isOpen;
+} // DiskIO::OpenForRead(void)
+
+// An extended file-open function. This includes some system-specific checks.
+// Returns 1 if the file is open, 0 otherwise....
+int DiskIO::OpenForWrite(void) {
+   if ((isOpen) && (openForWrite))
+      return 1;
+
+   // Close the disk, in case it's already open for reading only....
+   Close();
+
+   // try to open the device; may fail....
+   fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
+                   FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
+                   FILE_ATTRIBUTE_NORMAL, NULL);
+   // Preceding call can fail when creating backup files; if so, try
+   // again with different option...
+   if (fd == INVALID_HANDLE_VALUE) {
+      CloseHandle(fd);
+      fd = CreateFile(realFilename.c_str(), GENERIC_READ | GENERIC_WRITE,
+                      FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS,
+                      FILE_ATTRIBUTE_NORMAL, NULL);
+   } // if
+   if (fd == INVALID_HANDLE_VALUE) {
+      CloseHandle(fd);
+      isOpen = 0;
+      openForWrite = 0;
+      errno = GetLastError();
+   } else {
+      isOpen = 1;
+      openForWrite = 1;
+   } // if/else
+   return isOpen;
+} // DiskIO::OpenForWrite(void)
+
+// Close the disk device. Note that this does NOT erase the stored filenames,
+// so the file can be re-opened without specifying the filename.
+void DiskIO::Close(void) {
+   if (isOpen)
+      CloseHandle(fd);
+   isOpen = 0;
+   openForWrite = 0;
+} // DiskIO::Close()
+
+// Returns block size of device pointed to by fd file descriptor. If the ioctl
+// 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) {
+   DWORD blockSize = 0, retBytes;
+   DISK_GEOMETRY_EX geom;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      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;
+      } // if/else
+   } // if (isOpen)
+
+   return (blockSize);
+} // DiskIO::GetBlockSize()
+
+// Returns the number of heads, according to the kernel, or 255 if the
+// correct value can't be determined.
+uint32_t DiskIO::GetNumHeads(void) {
+   return UINT32_C(255);
+} // DiskIO::GetNumHeads();
+
+// Returns the number of sectors per track, according to the kernel, or 63
+// if the correct value can't be determined.
+uint32_t DiskIO::GetNumSecsPerTrack(void) {
+   return UINT32_C(63);
+} // DiskIO::GetNumSecsPerTrack()
+
+// Resync disk caches so the OS uses the new partition table. This code varies
+// a lot from one OS to another.
+// Returns 1 on success, 0 if the kernel continues to use the old partition table.
+int DiskIO::DiskSync(void) {
+   DWORD i;
+   GET_LENGTH_INFORMATION buf;
+   int retval = 0;
+
+   // If disk isn't open, try to open it....
+   if (!openForWrite) {
+      OpenForWrite();
+   } // if
+
+   if (isOpen) {
+      if (DeviceIoControl(fd, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, &buf, sizeof(buf), &i, NULL) == 0) {
+         cout << "Disk synchronization failed! The computer may use the old partition table\n"
+              << "until you reboot or remove and re-insert the disk!\n";
+      } else {
+         cout << "Disk synchronization succeeded! The computer should now use the new\n"
+              << "partition table.\n";
+         retval = 1;
+      } // if/else
+   } else {
+      cout << "Unable to open the disk for synchronization operation! The computer will\n"
+           << "continue to use the old partition table until you reboot or remove and\n"
+           << "re-insert the disk!\n";
+   } // if (isOpen)
+   return retval;
+} // DiskIO::DiskSync()
+
+// Seek to the specified sector. Returns 1 on success, 0 on failure.
+int DiskIO::Seek(uint64_t sector) {
+   int retval = 1;
+   LARGE_INTEGER seekTo;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      retval = OpenForRead();
+   } // if
+
+   if (isOpen) {
+      seekTo.QuadPart = sector * (uint64_t) GetBlockSize();
+      retval = SetFilePointerEx(fd, seekTo, NULL, FILE_BEGIN);
+      if (retval == 0) {
+         errno = GetLastError();
+         cerr << "Error when seeking to " << seekTo.QuadPart << "! Error is " << errno << "\n";
+         retval = 0;
+      } // if
+   } // if
+   return retval;
+} // DiskIO::Seek()
+
+// A variant on the standard read() function. Done to work around
+// limitations in FreeBSD concerning the matching of the sector
+// size with the number of bytes read.
+// Returns the number of bytes read into buffer.
+int DiskIO::Read(void* buffer, int numBytes) {
+   int blockSize = 512, i, numBlocks;
+   char* tempSpace;
+   DWORD retval = 0;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      // Compute required space and allocate memory
+      blockSize = GetBlockSize();
+      if (numBytes <= blockSize) {
+         numBlocks = 1;
+         tempSpace = new char [blockSize];
+      } else {
+         numBlocks = numBytes / blockSize;
+         if ((numBytes % blockSize) != 0)
+            numBlocks++;
+         tempSpace = new char [numBlocks * blockSize];
+      } // if/else
+      if (tempSpace == NULL) {
+         cerr << "Unable to allocate memory in DiskIO::Read()! Terminating!\n";
+         exit(1);
+      } // if
+
+      // Read the data into temporary space, then copy it to buffer
+      ReadFile(fd, tempSpace, numBlocks * blockSize, &retval, NULL);
+      for (i = 0; i < numBytes; i++) {
+         ((char*) buffer)[i] = tempSpace[i];
+      } // for
+
+      // Adjust the return value, if necessary....
+      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
+         retval = numBytes;
+
+      delete[] tempSpace;
+   } // if (isOpen)
+   return retval;
+} // DiskIO::Read()
+
+// A variant on the standard write() function.
+// Returns the number of bytes written.
+int DiskIO::Write(void* buffer, int numBytes) {
+   int blockSize = 512, i, numBlocks, retval = 0;
+   char* tempSpace;
+   DWORD numWritten;
+
+   // If disk isn't open, try to open it....
+   if ((!isOpen) || (!openForWrite)) {
+      OpenForWrite();
+   } // if
+
+   if (isOpen) {
+      // Compute required space and allocate memory
+      blockSize = GetBlockSize();
+      if (numBytes <= blockSize) {
+         numBlocks = 1;
+         tempSpace = new char [blockSize];
+      } else {
+         numBlocks = numBytes / blockSize;
+         if ((numBytes % blockSize) != 0) numBlocks++;
+         tempSpace = new char [numBlocks * blockSize];
+      } // if/else
+      if (tempSpace == NULL) {
+         cerr << "Unable to allocate memory in DiskIO::Write()! Terminating!\n";
+         exit(1);
+      } // if
+
+      // Copy the data to my own buffer, then write it
+      for (i = 0; i < numBytes; i++) {
+         tempSpace[i] = ((char*) buffer)[i];
+      } // for
+      for (i = numBytes; i < numBlocks * blockSize; i++) {
+         tempSpace[i] = 0;
+      } // for
+      WriteFile(fd, tempSpace, numBlocks * blockSize, &numWritten, NULL);
+      retval = (int) numWritten;
+
+      // Adjust the return value, if necessary....
+      if (((numBlocks * blockSize) != numBytes) && (retval > 0))
+         retval = numBytes;
+
+      delete[] tempSpace;
+   } // if (isOpen)
+   return retval;
+} // DiskIO:Write()
+
+// Returns the size of the disk in blocks.
+uint64_t DiskIO::DiskSize(int *err) {
+   uint64_t sectors = 0; // size in sectors
+   DWORD bytes, moreBytes; // low- and high-order bytes of file size
+   GET_LENGTH_INFORMATION buf;
+   DWORD i;
+
+   // If disk isn't open, try to open it....
+   if (!isOpen) {
+      OpenForRead();
+   } // if
+
+   if (isOpen) {
+      // Note to self: I recall testing a simplified version of
+      // this code, similar to what's in the __APPLE__ block,
+      // on Linux, but I had some problems. IIRC, it ran OK on 32-bit
+      // systems but not on 64-bit. Keep this in mind in case of
+      // 32/64-bit issues on MacOS....
+      if (DeviceIoControl(fd, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &buf, sizeof(buf), &i, NULL)) {
+         sectors = (uint64_t) buf.Length.QuadPart / GetBlockSize();
+         *err = 0;
+      } else { // doesn't seem to be a disk device; assume it's an image file....
+         bytes = GetFileSize(fd, &moreBytes);
+         sectors = ((uint64_t) bytes + ((uint64_t) moreBytes) * UINT32_MAX) / GetBlockSize();
+         *err = 0;
+      } // if
+   } else {
+      *err = -1;
+      sectors = 0;
+   } // if/else (isOpen)
+
+   return sectors;
+} // DiskIO::DiskSize()
diff --git a/gptfdisk/diskio.cc b/gptfdisk/diskio.cc
new file mode 100644
index 0000000..f65011d
--- /dev/null
+++ b/gptfdisk/diskio.cc
@@ -0,0 +1,91 @@
+//
+// C++ Interface: diskio (platform-independent components)
+//
+// Description: Class to handle low-level disk I/O for GPT fdisk
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#ifdef _WIN32
+#include <windows.h>
+#include <winioctl.h>
+#define fstat64 fstat
+#define stat64 stat
+#define S_IRGRP 0
+#define S_IROTH 0
+#else
+#include <sys/ioctl.h>
+#endif
+#include <string>
+#include <stdint.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <iostream>
+
+#include "support.h"
+#include "diskio.h"
+//#include "gpt.h"
+
+using namespace std;
+
+DiskIO::DiskIO(void) {
+   userFilename = "";
+   realFilename = "";
+   isOpen = 0;
+   openForWrite = 0;
+} // constructor
+
+DiskIO::~DiskIO(void) {
+   Close();
+} // destructor
+
+// Open a disk device for reading. Returns 1 on success, 0 on failure.
+int DiskIO::OpenForRead(const string & filename) {
+   int shouldOpen = 1;
+
+   if (isOpen) { // file is already open
+      if (((realFilename != filename) && (userFilename != filename)) || (openForWrite)) {
+         Close();
+      } else {
+         shouldOpen = 0;
+      } // if/else
+   } // if
+
+   if (shouldOpen) {
+      userFilename = filename;
+      MakeRealName();
+      OpenForRead();
+   } // if
+
+   return isOpen;
+} // DiskIO::OpenForRead(string filename)
+
+// Open a disk for reading and writing by filename.
+// Returns 1 on success, 0 on failure.
+int DiskIO::OpenForWrite(const string & filename) {
+   int retval = 0;
+
+   if ((isOpen) && (openForWrite) && ((filename == realFilename) || (filename == userFilename))) {
+      retval = 1;
+   } else {
+      userFilename = filename;
+      MakeRealName();
+      retval = OpenForWrite();
+      if (retval == 0) {
+         realFilename = userFilename = "";
+      } // if
+   } // if/else
+   return retval;
+} // DiskIO::OpenForWrite(string filename)
diff --git a/gptfdisk/diskio.h b/gptfdisk/diskio.h
new file mode 100644
index 0000000..631a43a
--- /dev/null
+++ b/gptfdisk/diskio.h
@@ -0,0 +1,83 @@
+//
+// C++ Interface: diskio
+//
+// Description: Class to handle low-level disk I/O for GPT fdisk
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
+
+#ifndef __DISKIO_H
+#define __DISKIO_H
+
+#include <string>
+#include <stdint.h>
+#include <sys/types.h>
+#ifdef _WIN32
+#include <windows.h>
+#include <winioctl.h>
+#else
+#include <sys/ioctl.h>
+#endif
+
+#ifdef __sun__
+#include <sys/dkio.h>
+#endif
+
+#if defined (__FreeBSD__) || defined (__FreeBSD_kernel__) || defined (__APPLE__)
+#define fstat64 fstat
+#define stat64 stat
+#endif
+
+#include "support.h"
+//#include "parttypes.h"
+
+using namespace std;
+
+/***************************************
+ *                                     *
+ * DiskIO class and related structures *
+ *                                     *
+ ***************************************/
+
+class DiskIO {
+   protected:
+      string userFilename;
+      string realFilename;
+      int isOpen;
+      int openForWrite;
+#ifdef _WIN32
+      HANDLE fd;
+#else
+      int fd;
+#endif
+   public:
+      DiskIO(void);
+      ~DiskIO(void);
+
+      void MakeRealName(void);
+      int OpenForRead(const string & filename);
+      int OpenForRead(void);
+      int OpenForWrite(const string & filename);
+      int OpenForWrite(void);
+      void Close();
+      int Seek(uint64_t sector);
+      int Read(void* buffer, int numBytes);
+      int Write(void* buffer, int numBytes);
+      int DiskSync(void); // resync disk caches to use new partitions
+      int GetBlockSize(void);
+      uint32_t GetNumHeads(void);
+      uint32_t GetNumSecsPerTrack(void);
+      int IsOpen(void) {return isOpen;}
+      int IsOpenForWrite(void) {return openForWrite;}
+      string GetName(void) const {return realFilename;}
+
+      uint64_t DiskSize(int* err);
+}; // class DiskIO
+
+#endif
diff --git a/gptfdisk/fixparts.8 b/gptfdisk/fixparts.8
new file mode 100644
index 0000000..9c4312f
--- /dev/null
+++ b/gptfdisk/fixparts.8
@@ -0,0 +1,282 @@
+.\" Copyright 2011-2015 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" May be distributed under the GNU General Public License
+.TH "FIXPARTS" "8" "1.0.1" "Roderick W. Smith" "FixParts Manual"
+.SH "NAME"
+fixparts \- MBR partition table repair utility
+.SH "SYNOPSIS"
+.BI "fixparts "
+.I device
+
+.SH "DESCRIPTION"
+
+FixParts (aka \fBfixparts\fR) is a text\-mode menu\-driven program for
+repairing certain types of problems with Master Boot Record (MBR) partition
+tables. The program has three design goals, although a few additional
+features are supported, as well:
+
+.TP 
+.B *
+It can remove stray GUID Partition Table (GPT) data, which can be left
+behind on a disk that was once used as a GPT disk but then incompletely
+converted to the more common (as of 2011) MBR form.
+
+.TP
+.B *
+It can repair mis-sized extended partitions \-\- either partitions that
+extend beyond the physical end of the disk or that overlap with nearby
+primary partitions. FixParts is designed in such a way that this type of
+repair occurs automatically, so if it's the only problem with your disk,
+you can launch the program and then immediately save the partition table,
+making no manual changes, and the program will fix the problem.
+
+.TP
+.B *
+You can change primary partitions into logical partitions or vice\-versa,
+within constraints imposed by the MBR data structures.
+
+.PP
+
+Additional features include the ability to change partition type codes or
+boot/active flags, to delete partitions, and to recompute CHS values. With
+the possible exception of recomputing CHS values, these secondary features
+are better performed with \fBfdisk\fR, because \fBfixparts\fR' design means
+that it's likely to alter partition numbering even when such changes are
+not requested.
+
+The \fBfixparts\fR program employs a user interface similar to that of
+Linux's \fBfdisk\fR, but \fBfixparts\fR is much more specialized. Most
+importantly, you can't create new partitions with \fBfixparts\fR, although
+you can change primary/logical assignment.
+
+In the MBR scheme, partitions come in three varieties:
+
+.TP
+.B primary
+These partitions are defined in the first sector of the hard disk and
+are limited in number to four. Some OSes, such as Windows and FreeBSD, must
+boot from a primary partition.
+
+.TP
+.B extended
+Extended partitions are specialized primary partitions. They serve as
+holding areas for logical partitions.
+
+.TP
+.B logical
+A disk can contain an arbitrary number of logical partitions
+(\fBfixparts\fR, however, imposes a limit of 124 logical partitions). All
+the logical partitions reside inside a single extended partition, and are
+defined using a linked-list data structure. This fact means that every
+logical partition must be preceded by at least one sector of unallocated space
+to hold its defining data structure (an Extended Boot Record, or EBR).
+
+.PP
+
+These distinctions mean that primary and logical partitions cannot be
+arbitrarily interspersed. A disk can contain one to three primary
+partitions, a block of one or more logical partitions, and one to three
+more primary partitions (for a total of three primary partitions, not
+counting the extended partition). Primary partitions may not be sandwiched
+between logical partitions, since this would mean placing a primary
+partition within an extended partition (which is just a specific type of
+primary partition).
+
+Unlike most disk utilities, \fBfixparts\fR' user interface ignores extended
+partitions. Internally, the program discards the information on the
+original extended partition and, when you tell it to save its changes, it
+generates a new extended partition to contain the then-defined logical
+partitions. This is done because most of the repairs and manipulations the
+tool performs require generating a fresh extended partition, so keeping the
+original in the user interface would only be a complication.
+
+Another unusual feature of \fBfixparts\fR' user interface is that partition
+numbers do not necessarily correlate with primary/logical status. In most
+utilities, partitions 1\-4 correspond to primary partitions, whereas
+partitions 5 and up are logical partitions. In \fBfixparts\fR, any partition
+number may be assigned primary or logical status, so long as the rules for
+layout described earlier are obeyed. When the partition table is saved,
+partitions will be assigned appropriately and then tools such as the Linux
+kernel and \fBfdisk\fR will give them conventional numbers.
+
+When it first starts, \fBfixparts\fR performs a scan for GPT data. If the
+disk looks like a conventional GPT disk, \fBfixparts\fR refuses to run. If
+the disk appears to be a conventional MBR disk but GPT signatures are
+present in the GPT primary or secondary header areas, \fBfixparts\fR
+offers to delete this extraneous data. If you tell it to do so, the program
+immediately wipes the GPT header or headers. (If only one header was found,
+only that one header will be erased, to minimize the risk of damaging a
+boot loader or other data that might have overwritten just one of the GPT
+headers.)
+
+With the exception of optionally erasing leftover GPT data when it first
+starts, \fBfixparts\fR keeps all changes in memory until the user writes
+changes with the \fBw\fR command. Thus, you can adjust your partitions in
+the user interface and abort those changes by typing \fBq\fR to quit
+without saving changes.
+
+.SH "OPTIONS"
+
+The \fBfixparts\fR utility supports no command\-line options, except for
+specification of the target device.
+
+Most interactions with \fBfixparts\fR occur with its interactive text\-mode
+menu. Specific functions are:
+
+.TP 
+.B a
+Toggle the active/boot flag. This flag is required by some boot loaders and
+OSes.
+
+
+.TP 
+.B c
+Recompute the cylinder/head/sector (CHS) values for all partitions. CHS
+addressing mode is largely obsolete, but some OSes and utilities complain
+if they don't like the CHS values. Note that \fBfixparts\fR' CHS values are
+likely to be incorrect on disks smaller than about 8 GiB except on Linux.
+
+.TP 
+.B l
+Change a partition's status to logical. This option will only work if the
+current partition layout supports such a change. Note that if changing a
+partition's status in this way is not currently possible, making some other
+change may make it possible. For instance, omitting a partition that
+precedes the target partition may enable converting a partition to logical
+form if there had been no free sectors between the two partitions.
+
+.TP 
+.B o
+Omit a partition. Once omitted, the partition will still appear in the
+\fBfixparts\fR partition list, but it will be flagged as omitted. You can
+subsequently convert it to primary or logical form with the \fBr\fR or
+\fBl\fR commands, respectively. When you save your changes with \fBw\fR,
+though, the partition will be lost.
+
+.TP 
+.B p
+Display basic partition summary data. This includes partition's number, the
+boot/active flag's status, starting and ending sector numbers,
+primary/logical/omitted status, whether or not the partition may be
+converted to logical form, and the partition's MBR types code.
+
+.TP 
+.B q
+Quit from the program \fIwithout saving your changes\fR.
+Use this option if you just wanted to view information or if you make a
+mistake and want to back out of all your changes.
+
+.TP 
+.B r
+Change a partition's status to primary. This option will only work if the
+current partition layout supports such a change. Note that every partition
+can theoretically become a primary partition, although in some
+configurations, making this change will require omitting some partitions.
+If \fBfixparts\fR refuses to allow changing a partition to primary, you may
+need to convert other partitions to logical form or omit them entirely.
+
+.TP 
+.B s
+Sort partition entries. This option orders partitions in the display to
+match their on-disk positions, which can make understanding the disk layout
+easier in some cases. This option has no effect on the ultimate ordering of
+logical partitions, which are sorted before being saved. The order of
+primary partitions in the final saved partition table may be affected by
+this option. In both cases, as already noted, the partition numbers
+displayed by \fBfixparts\fR may not be the same as those used by the kernel
+or displayed by other partitioning tools.
+
+.TP 
+.B t
+Change a partition's type code. You enter the type code using a one\-byte
+hexadecimal number.
+
+.TP 
+.B w
+Write data. Use this command to save your changes and exit from the program.
+
+.TP 
+.B ?
+Print the menu. Type this command (or any other unrecognized command) to
+see a summary of available options.
+
+.PP 
+
+.SH "BUGS"
+Known bugs and limitations include:
+
+.TP 
+.B *
+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. Under FreeBSD,
+32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit
+versions for Mac OS X and Windows have been tested.
+
+.TP 
+.B *
+The FreeBSD version of the program can't write changes to the partition
+table to a disk when existing partitions on that disk are mounted. (The
+same problem exists with many other FreeBSD utilities, such as
+\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome
+by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt.
+
+.TP 
+.B *
+The program can load only up to 128 partitions (4 primary partitions and
+124 logical partitions). This limit can be raised by changing the
+\fI#define MAX_MBR_PARTS\fR line in the \fIbasicmbr.h\fR source code file
+and recompiling.
+
+.TP 
+.B *
+The program can read partitions only if the disk has correct LBA partition
+descriptors. These descriptors should be present on any disk over 8 GiB in
+size or on smaller disks partitioned with any but very ancient software.
+
+.TP 
+.B *
+The program makes no effort to preserve partition numbers. This can have
+consequences for boot loaders and for mounting filesystems via
+\fB/etc/fstab\fR. It may be necessary to edit configuration files or even
+to re-install your boot loader.
+
+.TP
+.B *
+
+The program may change the order of partitions in the partition table.
+
+.PP 
+
+.SH "AUTHORS"
+Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
+
+Contributors:
+
+* Yves Blusseau (1otnwmz02@sneakemail.com)
+
+* David Hubbard (david.c.hubbard@gmail.com)
+
+* Justin Maggard (justin.maggard@netgear.com)
+
+* Dwight Schauer (dschauer@gmail.com)
+
+* Florian Zumbiehl (florz@florz.de)
+
+
+.SH "SEE ALSO"
+\fBcfdisk (8)\fR,
+\fBcgdisk (8)\fR,
+\fBfdisk (8)\fR,
+\fBmkfs (8)\fR,
+\fBparted (8)\fR,
+\fBsfdisk (8)\fR
+\fBgdisk (8)\fR
+\fBsgdisk (8)\fR
+
+\fIhttp://en.wikipedia.org/wiki/Master_boot_record\fR
+
+\fIhttp://www.rodsbooks.com/fixparts/\fR
+
+.SH "AVAILABILITY"
+The \fBfixparts\fR command is part of the \fIGPT fdisk\fR package and is
+available from Rod Smith.
diff --git a/gptfdisk/fixparts.cc b/gptfdisk/fixparts.cc
new file mode 100644
index 0000000..318c65e
--- /dev/null
+++ b/gptfdisk/fixparts.cc
@@ -0,0 +1,114 @@
+// fixparts
+// Program to fix certain types of damaged Master Boot Record (MBR) partition
+// tables
+//
+// Copyright 2011 by Roderick W. Smith
+//
+// This program is distributed under the terms of the GNU GPL, as described
+// in the COPYING file.
+//
+// Based on C++ classes originally created for GPT fdisk (gdisk and sgdisk)
+// programs
+
+#include <stdio.h>
+#include <string.h>
+#include <string>
+#include <iostream>
+#include <sstream>
+#include "basicmbr.h"
+#include "support.h"
+
+using namespace std;
+
+void DoMBR(BasicMBRData & mbrTable);
+
+int main(int argc, char* argv[]) {
+   BasicMBRData mbrTable;
+   string device;
+
+   cout << "FixParts " << GPTFDISK_VERSION << "\n";
+   
+   switch (argc) {
+      case 1:
+         cout << "Type device filename, or press <Enter> to exit: ";
+         device = ReadString();
+         if (device.length() == 0)
+            exit(0);
+         break;
+      case 2:
+         device = argv[1];
+         break;
+      default:
+         cerr << "Usage: " << argv[0] << " device_filename\n";
+         exit(1);
+   } // switch
+
+   cout << "\nLoading MBR data from " << device << "\n";
+   if (!mbrTable.ReadMBRData(device)) {
+      cerr << "\nUnable to read MBR data from '" << device << "'! Exiting!\n\n";
+      exit(1);
+   } // if
+
+   // This switch() statement weeds out disks with GPT signatures and non-MBR
+   // disks so we don't accidentally damage them....
+   switch(mbrTable.GetValidity()) {
+      case hybrid: case gpt:
+         cerr << "\nThis disk appears to be a GPT disk. Use GNU Parted or GPT fdisk on it!\n";
+         cerr << "Exiting!\n\n";
+         exit(1);
+         break;
+      case invalid:
+         cerr << "\nCannot find valid MBR data on '" << device << "'! Exiting!\n\n";
+         exit(1);
+         break;
+      case mbr:
+         DoMBR(mbrTable);
+         break;
+      default:
+         cerr << "\nCannot determine the validity of the disk on '" << device
+              << "'! Exiting!\n\n";
+         exit(1);
+         break;
+   } // switch()
+   return 0;
+} // main()
+
+// Do the bulk of the processing on actual MBR disks. First checks for old
+// GPT data (note this is different from the earlier check; this one only
+// looks for the GPT signatures in the main and backup GPT area, not for
+// a protective partition in the MBR, which we know is NOT present, since
+// if it were, this function would NOT be called!) and offers to destroy
+// it, if found; then makes sure the partitions are in a consistent and
+// legal state; then presents the MBR menu and, if it returns a "1" value
+// (meaning the user opted to write changes), writes the table to disk.
+void DoMBR(BasicMBRData & mbrTable) {
+   int doItAgain;
+
+   if (mbrTable.CheckForGPT() > 0) {
+      cout << "\nNOTICE: GPT signatures detected on the disk, but no 0xEE protective "
+           << "partition!\nThe GPT signatures are probably left over from a previous "
+           << "partition table.\nDo you want to delete them (if you answer 'Y', this "
+           << "will happen\nimmediately)? ";
+      if (GetYN() == 'Y') {
+         cout << "Erasing GPT data!\n";
+         if (mbrTable.BlankGPTData() != 1)
+            cerr << "GPT signature erasure failed!\n";
+      } // if
+   } // if
+
+   mbrTable.MakeItLegal();
+   do {
+      doItAgain = 0;
+      if (mbrTable.DoMenu() > 0) {
+         cout << "\nFinal checks complete. About to write MBR data. THIS WILL OVERWRITE "
+              << "EXISTING\nPARTITIONS!!\n\nDo you want to proceed? ";
+         if (GetYN() == 'Y') {
+            mbrTable.WriteMBRData();
+            mbrTable.DiskSync();
+            doItAgain = 0;
+         } else {
+            doItAgain = 1;
+         } // else
+      } // if
+   } while (doItAgain);
+} // DoMBR()
diff --git a/gptfdisk/gdisk.8 b/gptfdisk/gdisk.8
new file mode 100644
index 0000000..c33ca5f
--- /dev/null
+++ b/gptfdisk/gdisk.8
@@ -0,0 +1,687 @@
+.\" Copyright 2011-2015 Roderick W. Smith (rodsmith@rodsbooks.com)
+.\" May be distributed under the GNU General Public License
+.TH "GDISK" "8" "1.0.1" "Roderick W. Smith" "GPT fdisk Manual"
+.SH "NAME"
+gdisk \- Interactive GUID partition table (GPT) manipulator
+.SH "SYNOPSIS"
+.BI "gdisk "
+[ \-l ]
+.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
+disklabel stored without an MBR carrier partition to the newer Globally
+Unique Identifier (GUID) Partition Table (GPT) format, or will load a GUID
+partition table. When used with the \fI\-l\fR command\-line option, the
+program displays the current partition table and then exits.
+
+GPT fdisk operates mainly on the GPT headers and partition tables; however,
+it can and will generate a fresh protective MBR, when required. (Any boot
+loader code in the protective MBR will not be disturbed.) If you've created
+an unusual protective MBR, such as a hybrid MBR created by
+\fBgptsync\fR or \fBgdisk\fR's own hybrid MBR creation feature,
+this should not be disturbed by most ordinary actions. Some advanced data
+recovery options require you to understand the distinctions between the
+main and backup data, as well as between the GPT headers and the partition
+tables. For information on MBR vs. GPT, as well as GPT terminology and
+structure, see the extended \fBgdisk\fR documentation at
+\fIhttp://www.rodsbooks.com/gdisk/\fR or consult Wikipedia.
+
+The \fBgdisk\fR program employs a user interface similar to that of Linux's
+\fBfdisk\fR, but \fBgdisk\fR modifies GPT partitions. It also has the
+capability of transforming MBR partitions or BSD disklabels into GPT
+partitions. Like the original \fBfdisk\fR program, \fBgdisk\fR does not
+modify disk structures until you explicitly write them to disk, so if you
+make a mistake, you can exit from the program with the 'q' option to leave
+your partitions unmodified.
+
+Ordinarily, \fBgdisk\fR operates on disk device files, such as
+\fI/dev/sda\fR or \fI/dev/hda\fR under Linux, \fI/dev/disk0\fR under
+Mac OS X, or \fI/dev/ad0\fR or \fI/dev/da0\fR under FreeBSD. The program
+can also operate on disk image files, which can be either copies of whole
+disks (made with \fBdd\fR, for instance) or raw disk images used by
+emulators such as QEMU or VMWare. Note that only \fIraw\fR disk images
+are supported; \fBgdisk\fR cannot work on compressed or other advanced
+disk image formats.
+
+The MBR partitioning system uses a combination of cylinder/head/sector
+(CHS) addressing and logical block addressing (LBA). The former is klunky
+and limiting. GPT drops CHS addressing and uses 64\-bit LBA mode
+exclusively. Thus, GPT data structures, and therefore
+\fBgdisk\fR, do not need to deal with CHS geometries and all the problems
+they create. Users of \fBfdisk\fR will note that \fBgdisk\fR
+lacks the options and limitations associated with CHS geometries.
+
+For best results, you should use an OS\-specific partition table
+program whenever possible. For example, you should make Mac OS X
+partitions with the Mac OS X Disk Utility program and Linux partitions
+with the Linux \fBgdisk\fR or GNU Parted program.
+
+Upon start, \fBgdisk\fR attempts to identify the partition type in use on
+the disk. If it finds valid GPT data, \fBgdisk\fR will use it. If
+\fBgdisk\fR finds a valid MBR or BSD disklabel but no GPT data, it will
+attempt to convert the MBR or disklabel into GPT form. (BSD disklabels are
+likely to have unusable first and/or final partitions because they overlap
+with the GPT data structures, though.) GPT fdisk can identify, but not use
+data in, Apple Partition Map (APM) disks, which are used on 680x0\- and
+PowerPC\-based Macintoshes. Upon exiting with the 'w' option, \fBgdisk\fR
+replaces the MBR or disklabel with a GPT. \fIThis action is potentially
+dangerous!\fR Your system may become unbootable, and partition type codes
+may become corrupted if the disk uses unrecognized type codes. Boot
+problems are particularly likely if you're multi\-booting with any
+GPT\-unaware OS. If you mistakenly launch \fBgdisk\fR on an MBR disk, you
+can safely exit the program without making any changes by using the 'q'
+option.
+
+The MBR\-to\-GPT conversion will leave at least one gap in the partition
+numbering if the original MBR used logical partitions. These gaps are
+harmless, but you can eliminate them by using the 's' option, if you like.
+(Doing this may require you to update your \fI/etc/fstab\fR file.)
+
+When creating a fresh partition table, certain considerations may be in
+order:
+
+.TP 
+.B *
+For data (non\-boot) disks, and for boot disks used on BIOS\-based computers
+with GRUB as the boot loader, partitions may be created in whatever order
+and in whatever sizes are desired.
+
+.TP 
+.B *
+Boot disks for EFI\-based systems require an \fIEFI System
+Partition\fR (\fBgdisk\fR internal code 0xEF00) formatted as FAT\-32.
+The recommended size of this partition is between 100 and 300 MiB.
+Boot\-related files are stored here. (Note that GNU Parted identifies
+such partitions as having the "boot flag" set.)
+
+.TP 
+.B *
+Some boot loaders for BIOS\-based systems make use of a \fIBIOS Boot
+Partition\fR (\fBgdisk\fR internal code 0xEF02), in which the secondary
+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 *
+If Windows is to boot from a GPT disk, a partition of type \fIMicrosoft
+Reserved\fR (\fBgdisk\fR
+internal code 0x0C01) is recommended. This partition should be about 128 MiB
+in size. It ordinarily follows the EFI System Partition and immediately
+precedes the Windows data partitions. (Note that old versions of GNU Parted
+create all FAT partitions as this type, which actually makes the partition
+unusable for normal file storage in both Windows and Mac OS X.)
+
+.TP 
+.B *
+Some OSes' GPT utilities create some blank space (typically 128 MiB) after
+each partition. The intent is to enable future disk utilities to use this
+space. Such free space is not required of GPT disks, but creating it may
+help in future disk maintenance. You can use GPT fdisk's relative partition
+positioning option (specifying the starting sector as '+128M', for
+instance) to simplify creating such gaps.
+
+.SH "OPTIONS"
+.TP 
+.B \-l
+List the partition table for the specified device and then exits.
+.PP 
+
+Most interactions with \fBgdisk\fR
+occur with its interactive text\-mode menus. Three menus exist: the main
+menu, the recovery & transformation menu, and the experts' menu. The main
+menu provides the functions that are most likely to be useful for typical partitioning tasks, such as creating and deleting partitions, changing partition type codes, and so on. Specific functions are:
+
+.TP 
+.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
+file is a binary file consisting of the protective MBR, the main GPT
+header, the backup GPT header, and one copy of the partition table, in that
+order. Note that the backup is of the current in\-memory data structures, so
+if you launch the program, make changes, and then use this option, the
+backup will reflect your changes. Note also that the restore option is on
+the recovery & transformation menu; the backup option is on the main menu
+to encourage its use.
+
+
+.TP 
+.B c
+Change the GPT name of a partition. This name is encoded as a UTF\-16
+string, but proper entry and display of anything beyond basic ASCII values
+requires suitable locale and font support. For the most part, Linux ignores
+the partition name, but it may be important in some OSes. GPT fdisk sets a
+default name based on the partition type code. Note that the GPT partition
+name is different from the filesystem name, which is encoded in the
+filesystem's data structures.
+
+.TP 
+.B d
+Delete a partition. This action deletes the entry from the partition table
+but does not disturb the data within the sectors originally allocated to
+the partition on the disk. If a corresponding hybrid MBR partition exists,
+\fBgdisk\fR deletes it, as well, and expands any adjacent 0xEE (EFI GPT)
+MBR protective partition to fill the new free space.
+
+.TP 
+.B i
+Show detailed partition information. The summary information produced by
+the 'p' command necessarily omits many details, such as the partition's
+unique GUID and the translation of \fBgdisk\fR's
+internal partition type code to a plain type name. The 'i' option
+displays this information for a single partition.
+
+.TP 
+.B l
+Display a summary of partition types. GPT uses a GUID to identify partition
+types for particular OSes and purposes. For ease of data entry, \fBgdisk\fR
+compresses these into two\-byte (four\-digit hexadecimal) values that are
+related to their equivalent MBR codes. Specifically, the MBR code is
+multiplied by hexadecimal 0x0100. For instance, the code for Linux swap
+space in MBR is 0x82, and it's 0x8200 in \fBgdisk\fR. A one\-to\-one
+correspondence is impossible, though. Most notably, the codes for all
+varieties of FAT and NTFS partition correspond to a single GPT code
+(entered as 0x0700 in \fBsgdisk\fR). Some OSes use a single MBR code but
+employ many more codes in GPT. For these, \fBgdisk\fR adds code numbers
+sequentially, such as 0xa500 for a FreeBSD disklabel, 0xa501 for FreeBSD
+boot, 0xa502 for FreeBSD swap, and so on. Note that these two\-byte codes
+are unique to \fBgdisk\fR.
+
+.TP 
+.B n
+Create a new partition. This command is modelled after the equivalent
+\fBfdisk\fR option, although some differences exist. You enter a partition
+number, starting sector, and an ending sector. Both start and end sectors
+can be specified in absolute terms as sector numbers or as positions
+measured in kibibytes (K), mebibytes (M), gibibytes (G), tebibytes (T), or
+pebibytes (P); for instance, \fI\fB40M\fR\fR specifies a position 40MiB
+from the start of the disk. You can specify locations relative to the start
+or end of the specified default range by preceding the number by a '+' or '\-'
+symbol, as in \fI\fB+2G\fR\fR to specify a point 2GiB after the
+default start sector, or \fI\fB\-200M\fR\fR to specify a point 200MiB
+before the last available sector. Pressing the Enter key with no input
+specifies the default value, which is the start of the largest available
+block for the start sector and the end of the same block for the end
+sector.
+
+.TP 
+.B o
+Clear out all partition data. This includes GPT header data,
+all partition definitions, and the protective MBR. The sector alignment
+is reset to the default (1MB, or 2048 sectors on a disk with 512-byte
+sectors).
+
+.TP 
+.B p
+Display basic partition summary data. This includes partition
+numbers, starting and ending sector numbers, partition sizes,
+\fBgdisk\fR's partition types codes, and partition names. For
+additional information, use the 'i' command.
+
+.TP 
+.B q
+Quit from the program \fIwithout saving your changes\fR.
+Use this option if you just wanted to view information or if you make a
+mistake and want to back out of all your changes.
+
+.TP 
+.B r
+Enter the recovery & transformation menu. This menu includes emergency
+recovery options (to fix damaged GPT data structures) and options to
+transform to or from other partitioning systems, including creating
+hybrid MBRs.
+
+.TP 
+.B s
+Sort partition entries. GPT partition numbers need not match the order of
+partitions on the disk. If you want them to match, you can use this option.
+Note that some partitioning utilities sort partitions whenever they make
+changes. Such changes will be reflected in your device filenames, so you
+may need to edit \fI/etc/fstab\fR if you use this option.
+
+.TP 
+.B t
+Change a single partition's type code. You enter the type code using a
+two\-byte hexadecimal number, as described earlier. You may also enter a
+GUID directly, if you have one and \fBgdisk\fR doesn't know it.
+
+.TP 
+.B v
+Verify disk. This option checks for a variety of problems, such as
+incorrect CRCs and mismatched main and backup data. This option does not
+automatically correct most problems, though; for that, you must use
+options on the recovery & transformation menu. If no problems are found,
+this command displays a summary of unallocated disk space.
+
+.TP 
+.B w
+Write data. Use this command to save your changes.
+
+.TP 
+.B x
+Enter the experts' menu. Using this option provides access to features you
+can use to get into even more trouble than the main menu allows.
+.PP 
+
+.TP 
+.B ?
+Print the menu. Type this command (or any other unrecognized command) to
+see a summary of available options.
+
+.PP 
+The second \fBgdisk\fR menu is the recovery & transformation menu, which
+provides access to data recovery options and features related to the
+transformation of partitions between partitioning schemes (converting
+BSD disklabels into GPT partitions or creating hybrid MBRs, for instance).
+A few options on this menu duplicate functionality on the main
+menu, for the sake of convenience. The options on this menu are:
+
+.TP 
+.B b
+Rebuild GPT header from backup. You can use the backup GPT header to
+rebuild the main GPT header with this option. It's likely to be useful if
+your main GPT header was damaged or destroyed (say, by sloppy use of
+\fBdd\fR).
+
+.TP 
+.B c
+Load backup partition table. Ordinarily, \fBgdisk\fR
+uses only the main partition table (although the backup's integrity is
+checked when you launch the program). If the main partition table has been
+damaged, you can use this option to load the backup from disk and use it
+instead. Note that this will almost certainly produce no or strange
+partition entries if you've just converted an MBR disk to GPT format, since
+there will be no backup partition table on disk.
+
+.TP 
+.B d
+Use main GPT header and rebuild the backup. This option is likely to be
+useful if the backup GPT header has been damaged or destroyed.
+
+.TP 
+.B e
+Load main partition table. This option reloads the main partition table
+from disk. It's only likely to be useful if you've tried to use the backup
+partition table (via 'c') but it's in worse shape then the main partition
+table.
+
+.TP 
+.B f
+Load MBR and build fresh GPT from it. Use this option if your GPT is corrupt
+or conflicts with the MBR and you want to use the MBR as the basis for a new
+set of GPT partitions.
+
+.TP 
+.B g
+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 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
+Create a hybrid MBR. This is an ugly workaround that enables GPT\-unaware
+OSes, or those that can't boot from a GPT disk, to access up to three of
+the partitions on the disk by creating MBR entries for them. Note that
+these hybrid MBR entries 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 re\-create 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
+Show detailed partition information. This option is identical to the 'i'
+option on the main menu.
+
+.TP 
+.B l
+Load partition data from a backup file. This option is the reverse of the 'b'
+option on the main menu. Note that restoring partition data from anything
+but the original disk is not recommended.
+
+.TP 
+.B m
+Return to the main menu. This option enables you to enter main\-menu commands.
+
+.TP 
+.B o
+Print protective MBR data. You can see a summary of the protective MBR's
+partitions with this option. This may enable you to spot glaring problems
+or help identify the partitions in a hybrid MBR.
+
+.TP 
+.B p
+Print the partition table. This option is identical to the 'p' option in
+the main menu.
+
+.TP 
+.B q
+Quit without saving changes. This option is identical to the 'q' option in
+the main menu.
+
+.TP 
+.B t
+Transform BSD partitions into GPT partitions. This option works on BSD
+disklabels held within GPT (or converted MBR) partitions. Converted
+partitions' type codes are likely to need manual adjustment. \fBgdisk\fR
+will attempt to convert BSD disklabels stored on the main disk when
+launched, but this conversion is likely to produce first and/or last
+partitions that are unusable. The many BSD variants means that the
+probability of \fBgdisk\fR being unable to convert a BSD disklabel is
+high compared to the likelihood of problems with an MBR conversion.
+
+.TP 
+.B v
+Verify disk. This option is identical to the 'v' option in the main menu.
+
+.TP 
+.B w
+Write table to disk and exit. This option is identical to the 'w' option in
+the main menu.
+
+.TP 
+.B x
+Enter the experts' menu. This option is identical to the 'x' option in the
+main menu.
+
+.TP 
+.B ?
+Print the menu. This option (or any unrecognized entry) displays a summary
+of the menu options.
+
+.PP 
+The third \fBgdisk\fR menu is the experts' menu. This menu provides advanced
+options that aren't closely related to recovery or transformation between
+partitioning systems. Its options are:
+
+.TP 
+.B a
+Set attributes. GPT provides a 64\-bit attributes field that can be used to
+set features for each partition. \fBgdisk\fR supports four attributes:
+\fIsystem partition\fR, \fIread\-only\fR, \fIhidden\fR, and
+\fIdo not automount\fR. You can set other attributes, but their numbers
+aren't translated into anything useful. In practice, most OSes seem to
+ignore these attributes.
+
+.TP 
+.B c
+Change partition GUID. You can enter a custom unique GUID for a partition
+using this option. (Note this refers to the GUID that uniquely identifies a
+partition, not to its type code, which you can change with the 't' main\-menu
+option.) Ordinarily, \fBgdisk\fR assigns this number randomly; however,
+you might want to adjust the number manually if you've wound up with the
+same GUID on two partitions because of buggy GUID assignments (hopefully
+not in \fBgdisk\fR) or sheer incredible coincidence.
+
+.TP 
+.B d
+Display the sector alignment value. See the
+description of the 'l' option for more details.
+
+.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
+that follows the backup GPT data structures. This command moves the backup
+GPT data structures to the end of the disk, where they belong.
+
+.TP
+.B f
+Randomize the disk's GUID and all partitions' unique GUIDs (but not their
+partition type code GUIDs). This function may be used after cloning a disk
+with another utility in order to render all GUIDs once again unique.
+
+.TP 
+.B g
+Change disk GUID. Each disk has a unique GUID code, which \fBgdisk\fR
+assigns randomly upon creation of the GPT data structures. You can generate
+a fresh random GUID or enter one manually with this option.
+
+.TP
+.B h
+Recompute CHS values in protective or hybrid MBR. This option can sometimes
+help if a disk utility, OS, or BIOS doesn't like the CHS values used by the
+partitions in the protective or hybrid MBR. In particular, the GPT
+specification requires a CHS value of 0xFFFFFF for over-8GiB partitions,
+but this value is technically illegal by the usual standards. Some BIOSes
+hang if they encounter this value. This option will recompute a more normal
+CHS value -- 0xFEFFFF for over-8GiB partitions, enabling these BIOSes to
+boot.
+
+.TP 
+.B i
+Show detailed partition information. This option is identical to the 'i'
+option on the main menu.
+
+.TP 
+.B l
+Change the sector alignment value. Disks with more logical sectors per
+physical sectors (such as modern Advanced Format drives), some RAID
+configurations, and many SSD devices, can suffer performance problems if
+partitions are not aligned properly for their internal data structures. On
+new disks, GPT fdisk attempts to align partitions on 1MiB boundaries
+(2048\-sectors on disks with 512-byte sectors) by default, which optimizes
+performance for all of these disk types. On pre\-partitioned disks, GPT
+fdisk attempts to identify the alignment value used on that disk, but will
+set 8-sector alignment on disks larger than 300 GB even if lesser alignment
+values are detected. In either case, it can be changed by using this
+option.
+
+.TP 
+.B m
+Return to the main menu. This option enables you to enter main\-menu commands.
+
+.TP 
+.B n
+Create a new protective MBR. Use this option if the current protective MBR
+is damaged in a way that \fBgdisk\fR doesn't automatically detect and
+correct, or if you want to convert a hybrid MBR into a "pure" GPT with a
+conventional protective MBR.
+
+.TP 
+.B o
+Print protective MBR data. You can see a summary of the protective MBR's
+partitions with this option. This may enable you to spot glaring problems
+or help identify the partitions in a hybrid MBR.
+
+.TP 
+.B p
+Print the partition table. This option is identical to the 'p' option in
+the main menu.
+
+.TP 
+.B q
+Quit without saving changes. This option is identical to the 'q' option in
+the main menu.
+
+.TP 
+.B r
+Enter the recovery & transformations menu. This option is identical to
+the 'r' option on the main menu.
+
+.TP 
+.B s
+Resize partition table. The default partition table size is 128 entries.
+Officially, sizes of less than 16KB (128 entries, given the normal entry
+size) are unsupported by the GPT specification; however, in practice they
+seem to work, and can sometimes be useful in converting MBR disks. Larger
+sizes also work fine. OSes may impose their own limits on the number of
+partitions, though.
+
+.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
+results in a table with partitions numbered from 2\-5. Transposing
+partitions in this way has no effect on their disk space allocation; it
+only alters their order in the partition table.
+
+.TP
+.B u
+Replicate the current device's partition table on another device. You will
+be prompted to type the new device's filename. After the write operation
+completes, you can continue editing the original device's partition table.
+Note that the replicated partition table is an exact copy, including all
+GUIDs; if the device should have its own unique GUIDs, you should use the
+\fBf\fR option on the new disk.
+
+.TP 
+.B v
+Verify disk. This option is identical to the 'v' option in the main menu.
+
+.TP 
+.B z
+Zap (destroy) the GPT data structures and exit. Use this option if you want to
+repartition a GPT disk using \fBfdisk\fR or some other GPT\-unaware program.
+You'll be given the choice of preserving the existing MBR, in case it's a
+hybrid MBR with salvageable partitions or if you've already created new MBR
+partitions and want to erase the remnants of your GPT partitions. \fIIf you've
+already created new MBR partitions, it's conceivable that this option will
+damage the first and/or last MBR partitions!\fR Such an event is unlikely, but
+could occur if your new MBR partitions overlap the old GPT data structures.
+
+.TP 
+.B ?
+Print the menu. This option (or any unrecognized entry) displays a summary
+of the menu options.
+
+.PP 
+In many cases, you can press the Enter key to select a default option when
+entering data. When only one option is possible, \fBgdisk\fR
+usually bypasses the prompt entirely.
+
+.SH "BUGS"
+Known bugs and limitations include:
+
+.TP 
+.B *
+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. Under FreeBSD,
+32\-bit (x86) and 64\-bit (x86\-64) versions have been tested. Only 32\-bit
+versions for Mac OS X and Windows have been tested by the author, although
+I've heard of 64-bit versions being successfully compiled.
+
+.TP 
+.B *
+The FreeBSD version of the program can't write changes to the partition
+table to a disk when existing partitions on that disk are mounted. (The
+same problem exists with many other FreeBSD utilities, such as
+\fBgpt\fR, \fBfdisk\fR, and \fBdd\fR.) This limitation can be overcome
+by typing \fBsysctl kern.geom.debugflags=16\fR at a shell prompt.
+
+.TP 
+.B *
+The fields used to display the start and end sector numbers for partitions
+in the 'p' command are 14 characters wide. This translates to a limitation
+of about 45 PiB. On larger disks, the displayed columns will go out of
+alignment.
+
+.TP 
+.B *
+In the Windows version, only ASCII characters are supported in the
+partition name field. If an existing partition uses non\-ASCII UTF\-16
+characters, they're likely to be corrupted in the 'i' and 'p' menu options'
+displays; however, they should be preserved when loading and saving
+partitions. Binaries for Linux, FreeBSD, and OS X support full UTF-16
+partition names.
+
+.TP 
+.B *
+The program can load only up to 128 partitions (4 primary partitions and
+124 logical partitions) when converting from MBR format. This limit can
+be raised by changing the \fI#define MAX_MBR_PARTS\fR line in the
+\fIbasicmbr.h\fR source code file and recompiling; however, such a change
+will require using a larger\-than\-normal partition table. (The limit
+of 128 partitions was chosen because that number equals the 128 partitions
+supported by the most common partition table size.)
+
+.TP 
+.B *
+Converting from MBR format sometimes fails because of insufficient space at
+the start or (more commonly) the end of the disk. Resizing the partition
+table (using the 's' option in the experts' menu) can sometimes overcome
+this problem; however, in extreme cases it may be necessary to resize a
+partition using GNU Parted or a similar tool prior to conversion with
+\fBgdisk\fR.
+
+.TP 
+.B *
+MBR conversions work only if the disk has correct LBA partition
+descriptors. These descriptors should be present on any disk over 8 GiB in
+size or on smaller disks partitioned with any but very ancient software.
+
+.TP 
+.B *
+BSD disklabel support can create first and/or last partitions that overlap
+with the GPT data structures. This can sometimes be compensated by
+adjusting the partition table size, but in extreme cases the affected
+partition(s) may need to be deleted.
+
+.TP 
+.B *
+Because of the highly variable nature of BSD disklabel structures,
+conversions from this form may be unreliable \-\- partitions may be dropped,
+converted in a way that creates overlaps with other partitions, or
+converted with incorrect start or end values. Use this feature with
+caution!
+
+.TP 
+.B *
+Booting after converting an MBR or BSD disklabel disk is likely to be
+disrupted. Sometimes re\-installing a boot loader will fix the problem, but
+other times you may need to switch boot loaders. Except on EFI\-based
+platforms, Windows through at least Windows 7 doesn't support booting
+from GPT disks. Creating a hybrid MBR (using the 'h' option on the recovery &
+transformation menu) or abandoning GPT in favor of MBR may be your only
+options in this case.
+
+.PP 
+
+.SH "AUTHORS"
+Primary author: Roderick W. Smith (rodsmith@rodsbooks.com)
+
+Contributors:
+
+* Yves Blusseau (1otnwmz02@sneakemail.com)
+
+* David Hubbard (david.c.hubbard@gmail.com)
+
+* Justin Maggard (justin.maggard@netgear.com)
+
+* Dwight Schauer (dschauer@gmail.com)
+
+* Florian Zumbiehl (florz@florz.de)
+
+
+.SH "SEE ALSO"
+\fBcfdisk (8)\fR,
+\fBcgdisk (8)\fR,
+\fBfdisk (8)\fR,
+\fBmkfs (8)\fR,
+\fBparted (8)\fR,
+\fBsfdisk (8)\fR
+\fBsgdisk (8)\fR
+\fBfixparts (8)\fR
+
+\fIhttp://en.wikipedia.org/wiki/GUID_Partition_Table\fR
+
+\fIhttp://developer.apple.com/technotes/tn2006/tn2166.html\fR
+
+\fIhttp://www.rodsbooks.com/gdisk/\fR
+
+.SH "AVAILABILITY"
+The \fBgdisk\fR command is part of the \fIGPT fdisk\fR package and is
+available from Rod Smith.
diff --git a/gptfdisk/gdisk.cc b/gptfdisk/gdisk.cc
new file mode 100644
index 0000000..5f85498
--- /dev/null
+++ b/gptfdisk/gdisk.cc
@@ -0,0 +1,69 @@
+// gdisk.cc
+// Program modelled after Linux fdisk, but it manipulates GPT partitions
+// rather than MBR partitions.
+//
+// by Rod Smith, project began February 2009
+
+/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#include <string.h>
+#include <iostream>
+#include "gpttext.h"
+
+int main(int argc, char* argv[]) {
+   GPTDataTextUI theGPT;
+   string device = "";
+   UnicodeString uString;
+   int isError = 0;
+
+#ifndef EFI
+   cout << "GPT fdisk (gdisk) version " << GPTFDISK_VERSION << "\n\n";
+#endif /*EFI*/
+
+   if (!SizesOK())
+      exit(1);
+
+   switch (argc) {
+      case 1:
+         cout << "Type device filename, or press <Enter> to exit: ";
+         device = ReadString();
+         if (device.length() == 0)
+            exit(0);
+         else if (theGPT.LoadPartitions(device)) {
+            if (theGPT.GetState() != use_gpt)
+               WinWarning();
+            theGPT.MainMenu(device);
+         } // if/elseif
+         break;
+      case 2: // basic usage
+         if (theGPT.LoadPartitions(argv[1])) {
+            if (theGPT.GetState() != use_gpt)
+               WinWarning();
+            theGPT.MainMenu(argv[1]);
+         } // if
+         break;
+      case 3: // usage with "-l" option
+         if (strcmp(argv[1], "-l") == 0) {
+            device = (string) argv[2];
+         } else if (strcmp(argv[2], "-l") == 0) {
+            device = (string) argv[1];
+         } else { // 3 arguments, but none is "-l"
+            cerr << "Usage: " << argv[0] << " [-l] device_file\n";
+            isError = 1;
+         } // if/elseif/else
+         if (device != "") {
+            theGPT.JustLooking();
+            if (theGPT.LoadPartitions(device))
+               theGPT.DisplayGPTData();
+            else
+               isError = 1;
+         } // if
+         break;
+      default:
+         cerr << "Usage: " << argv[0] << " [-l] device_file\n";
+         isError = 1;
+         break;
+   } // switch
+   return (isError);
+} // main
diff --git a/gptfdisk/gdisk_test.sh b/gptfdisk/gdisk_test.sh
new file mode 100755
index 0000000..25e2651
--- /dev/null
+++ b/gptfdisk/gdisk_test.sh
@@ -0,0 +1,402 @@
+#!/bin/bash
+# test gdisk and sgdisk by creating a dd file
+# Copyright (C) 2011 Guillaume Delacour <gui@iroqwa.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along
+# with this program; if not, write to the Free Software Foundation, Inc.,
+# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#
+# Requires: coreutils (mktemp, dd) and 64M of disk space in /tmp (temp dd disk)
+#
+# This script test gdisk commands through the following scenario:
+# - Initialize a new GPT table
+# - Create a single Linux partition
+# - Change name of partition
+# - Change type of partition
+# - Backup to file the GPT table
+# - Delete the single partition
+# - Restore from backup file the GPT table
+# - Wipe the GPT table
+
+# TODO
+# Try to generate a wrong GPT table to detect problems (test --verify)
+# Create MBR partition table with fdisk and migrate it with gdisk
+
+GDISK_BIN=./gdisk
+SGDISK_BIN=./sgdisk
+
+OPT_CLEAR="o"
+OPT_NEW="n"
+OPT_CHANGE_NAME="c"
+OPT_CHANGE_TYPE="t"
+OPT_BACKUP="b"
+OPT_DELETE="d"
+OPT_ZAP="z"
+
+# temp disk for testing gdisk
+TEMP_DISK=$(mktemp)
+# 64 MiB
+TEMP_DISK_SIZE=65536
+
+# the test partition to create
+TEST_PART_TYPE="8300"
+TEST_PART_DEFAULT_NAME="Linux filesystem"
+
+# newname for the partition
+TEST_PART_NEWNAME=$(tr -dc "[:alpha:]" < /dev/urandom | head -c 8)
+# and new type (swap for example)
+TEST_PART_NEWTYPE="8200"
+
+# GPT data backup to filename
+GPT_BACKUP_FILENAME=$(mktemp)
+
+# Pretty print string (Red if FAILED or green if SUCCESS)
+# $1: string to pretty print
+pretty_print() {
+	if [ "$1" = "SUCCESS" ]
+	then
+		# green
+		color="32"
+	else
+		# red
+		color="31"
+	fi
+
+	printf "\033[0;${color}m**$1**\033[m $2\n"
+}
+
+# Verify that the partition exist and has the given type/name
+# $1: Partition type to verify (ex.: 8300)
+# $2: Partition name to verify (ex.: Linux filesystem)
+# $3: Text to print
+verify_part() {
+	partition=$($GDISK_BIN -l $TEMP_DISK | tail -n 1)
+	echo $partition | grep -q "$1[[:space:]]$2$"
+
+	if [ $? -eq 0 ]
+	then
+		pretty_print "SUCCESS" "$3"
+	else
+		pretty_print "FAILED" "$3"
+		exit 1
+	fi
+}
+
+
+#####################################
+# Get GUID of disk
+#####################################
+get_diskguid() {
+	DISK_GUID=$($GDISK_BIN -l $TEMP_DISK | grep "^Disk identifier (GUID):" | awk '{print $4}')
+	return $DISK_GUID
+}
+
+
+#####################################
+# Create a new empty table
+#####################################
+create_table() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_CLEAR
+Y
+w
+Y
+EOF
+
+			ret=$?
+			if [ $ret -ne 0 ]
+			then
+				pretty_print "FAILED" "gdisk return $ret when creating partition table"
+				exit 1
+			fi
+		;;
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_CLEAR}
+
+			ret=$?
+			if [ $ret -ne 0 ]
+			then
+				pretty_print "FAILED" "sgdisk return $ret when creating partition table"
+				exit 1
+			fi
+		;;
+	esac
+
+	# verify that the table is empty
+	# only the columns should appear in the table
+	verify_part "Code" "Name" "Create new empty GPT table"
+	echo ""
+}
+
+
+
+#####################################
+# First create a new partition
+#####################################
+create_partition() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_NEW
+1
+
+
+$TEST_PART_TYPE
+w
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_NEW} 1 -${OPT_CHANGE_NAME} 1:"${TEST_PART_DEFAULT_NAME}" -${OPT_CHANGE_TYPE} 1:$TEST_PART_TYPE
+		;;
+	esac
+
+	verify_part "$TEST_PART_TYPE" "$TEST_PART_DEFAULT_NAME" "Create new partition"
+	echo ""
+}
+
+
+#####################################
+# Change name of partition
+#####################################
+change_partition_name() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_CHANGE_NAME
+$TEST_PART_NEWNAME
+w
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_CHANGE_NAME} 1:${TEST_PART_NEWNAME}
+		;;
+	esac
+
+	verify_part "$TEST_PART_TYPE" "$TEST_PART_NEWNAME" "Change partition 1 name ($TEST_PART_DEFAULT_NAME -> $TEST_PART_NEWNAME)"
+	echo ""
+}
+
+
+change_partition_type() {
+#####################################
+# Change type of partition
+#####################################
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_CHANGE_TYPE
+$TEST_PART_NEWTYPE
+w
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_CHANGE_TYPE} 1:${TEST_PART_NEWTYPE}
+		;;
+	esac
+
+	verify_part "$TEST_PART_NEWTYPE" "$TEST_PART_NEWNAME" "Change partition 1 type ($TEST_PART_TYPE -> $TEST_PART_NEWTYPE)"
+	echo ""
+}
+
+
+#####################################
+# Backup GPT data to file
+#####################################
+backup_table() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_BACKUP
+$GPT_BACKUP_FILENAME
+q
+EOF
+echo ""
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_BACKUP} ${GPT_BACKUP_FILENAME}
+		;;
+	esac
+
+	# if exist and not empty; we will test it after
+	if [ -s $GPT_BACKUP_FILENAME ]
+	then
+		pretty_print "SUCCESS" "GPT data backuped sucessfully"
+	else
+		pretty_print "FAILED" "Unable to create GPT backup file !"
+		exit 1
+	fi
+}
+
+
+#####################################
+# Now, we can delete the partition
+#####################################
+delete_partition() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+$OPT_DELETE
+w
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_DELETE} 1
+		;;
+	esac
+
+	# verify that the table is empty (just one partition):
+	# only the columns should appear in the table
+	verify_part "Code" "Name" "Delete partition 1"
+	echo ""
+}
+
+
+#####################################
+# Restore GPT table
+#####################################
+restore_table() {
+	$GDISK_BIN $TEMP_DISK << EOF
+r
+r
+l
+$GPT_BACKUP_FILENAME
+w
+Y
+EOF
+
+	verify_part "$TEST_PART_NEWTYPE" "$TEST_PART_NEWNAME" "Restore the GPT backup"
+	echo ""
+}
+
+
+#####################################
+# Change UID of disk
+#####################################
+change_disk_uid() {
+
+	# get UID of disk before changing it
+	GUID=get_diskguid
+
+
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+x
+g
+R
+w
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -U=R
+		;;
+	esac
+
+	# get GUID after change
+	NEW_DISK_GUID=get_diskguid
+
+	# compare them
+	if [ "$DISK_GUID" != "$NEW_DISK_GUID" ]
+	then
+		pretty_print "SUCCESS" "GUID of disk has been sucessfully changed"
+	else
+		pretty_print "FAILED" "GUID of disk is the same as the previous one"
+		exit 1
+	fi
+}
+
+#####################################
+# Wipe GPT table
+#####################################
+wipe_table() {
+	case $1 in
+		gdisk)
+			$GDISK_BIN $TEMP_DISK << EOF
+x
+$OPT_ZAP
+Y
+Y
+EOF
+		;;
+
+		sgdisk)
+			$SGDISK_BIN $TEMP_DISK -${OPT_ZAP}
+	esac
+
+	# verify that the table is empty (just one partition):
+	# only the columns should appear in the table
+	verify_part "Code" "Name" "Wipe GPT table"
+	echo ""
+}
+
+#####################################
+# Test stdin EOF
+#####################################
+eof_stdin() {
+	$SGDISK_BIN $TEMP_DISK << EOF
+^D
+EOF
+	pretty_print "SUCCESS" "EOF successfully exit gdisk"
+}
+
+###################################
+# Main
+###################################
+
+# create a file to simulate a real device
+dd if=/dev/zero of=$TEMP_DISK bs=1024 count=$TEMP_DISK_SIZE > /dev/null 2>&1
+
+if [ -s $TEMP_DISK ]
+then
+	pretty_print "SUCCESS" "Temp disk sucessfully created"
+else
+	pretty_print "FAILED" "Unable to create temp disk !"
+	exit 1
+fi
+
+# test gdisk and sgdisk
+for binary in gdisk sgdisk
+do
+	echo ""
+	printf "\033[0;34m**Testing $binary binary**\033[m\n"
+	echo ""
+	create_table          "$binary"
+	create_partition      "$binary"
+	change_partition_name "$binary"
+	change_partition_type "$binary"
+	backup_table          "$binary"
+	delete_partition      "$binary"
+	restore_table         # only with gdisk
+	change_disk_uid       "$binary"
+	wipe_table            "$binary"
+	eof_stdin             # only with gdisk
+done
+
+# remove temp files
+rm -f $TEMP_DISK $GPT_BACKUP_FILENAME
+
+exit 0
diff --git a/gptfdisk/gpt.cc b/gptfdisk/gpt.cc
new file mode 100644
index 0000000..038e4f3
--- /dev/null
+++ b/gptfdisk/gpt.cc
@@ -0,0 +1,2471 @@
+/* gpt.cc -- Functions for loading, saving, and manipulating legacy MBR and GPT partition
+   data. */
+
+/* By Rod Smith, initial coding January to February, 2009 */
+
+/* This program is copyright (c) 2009-2013 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <fcntl.h>
+#include <string.h>
+#include <math.h>
+#include <time.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <iostream>
+#include <algorithm>
+#include "crc32.h"
+#include "gpt.h"
+#include "bsd.h"
+#include "support.h"
+#include "parttypes.h"
+#include "attributes.h"
+#include "diskio.h"
+
+using namespace std;
+
+#ifdef __FreeBSD__
+#define log2(x) (log(x) / M_LN2)
+#endif // __FreeBSD__
+
+#ifdef _MSC_VER
+#define log2(x) (log((double) x) / log(2.0))
+#endif // Microsoft Visual C++
+
+#ifdef EFI
+// in UEFI mode MMX registers are not yet available so using the
+// x86_64 ABI to move "double" values around is not an option.
+#ifdef log2
+#undef log2
+#endif
+#define log2(x) log2_32( x )
+static inline uint32_t log2_32(uint32_t v) {
+   int r = -1;
+   while (v >= 1) {
+      r++;
+      v >>= 1;
+   }
+   return r;
+}
+#endif
+
+/****************************************
+ *                                      *
+ * GPTData class and related structures *
+ *                                      *
+ ****************************************/
+
+// Default constructor
+GPTData::GPTData(void) {
+   blockSize = SECTOR_SIZE; // set a default
+   diskSize = 0;
+   partitions = NULL;
+   state = gpt_valid;
+   device = "";
+   justLooking = 0;
+   mainCrcOk = 0;
+   secondCrcOk = 0;
+   mainPartsCrcOk = 0;
+   secondPartsCrcOk = 0;
+   apmFound = 0;
+   bsdFound = 0;
+   sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
+   beQuiet = 0;
+   whichWasUsed = use_new;
+   mainHeader.numParts = 0;
+   numParts = 0;
+   SetGPTSize(NUM_GPT_ENTRIES);
+   // Initialize CRC functions...
+   chksum_crc32gentab();
+} // GPTData default constructor
+
+// The following constructor loads GPT data from a device file
+GPTData::GPTData(string filename) {
+   blockSize = SECTOR_SIZE; // set a default
+   diskSize = 0;
+   partitions = NULL;
+   state = gpt_invalid;
+   device = "";
+   justLooking = 0;
+   mainCrcOk = 0;
+   secondCrcOk = 0;
+   mainPartsCrcOk = 0;
+   secondPartsCrcOk = 0;
+   apmFound = 0;
+   bsdFound = 0;
+   sectorAlignment = MIN_AF_ALIGNMENT; // Align partitions on 4096-byte boundaries by default
+   beQuiet = 0;
+   whichWasUsed = use_new;
+   mainHeader.numParts = 0;
+   numParts = 0;
+   // Initialize CRC functions...
+   chksum_crc32gentab();
+   if (!LoadPartitions(filename))
+      exit(2);
+} // GPTData(string filename) constructor
+
+// Destructor
+GPTData::~GPTData(void) {
+   delete[] partitions;
+} // GPTData destructor
+
+// Assignment operator
+GPTData & GPTData::operator=(const GPTData & orig) {
+   uint32_t i;
+
+   mainHeader = orig.mainHeader;
+   numParts = orig.numParts;
+   secondHeader = orig.secondHeader;
+   protectiveMBR = orig.protectiveMBR;
+   device = orig.device;
+   blockSize = orig.blockSize;
+   diskSize = orig.diskSize;
+   state = orig.state;
+   justLooking = orig.justLooking;
+   mainCrcOk = orig.mainCrcOk;
+   secondCrcOk = orig.secondCrcOk;
+   mainPartsCrcOk = orig.mainPartsCrcOk;
+   secondPartsCrcOk = orig.secondPartsCrcOk;
+   apmFound = orig.apmFound;
+   bsdFound = orig.bsdFound;
+   sectorAlignment = orig.sectorAlignment;
+   beQuiet = orig.beQuiet;
+   whichWasUsed = orig.whichWasUsed;
+
+   myDisk.OpenForRead(orig.myDisk.GetName());
+
+   delete[] partitions;
+   partitions = new GPTPart [numParts];
+   if (partitions == NULL) {
+      cerr << "Error! Could not allocate memory for partitions in GPTData::operator=()!\n"
+           << "Terminating!\n";
+      exit(1);
+   } // if
+   for (i = 0; i < numParts; i++) {
+      partitions[i] = orig.partitions[i];
+   } // for
+
+   return *this;
+} // GPTData::operator=()
+
+/*********************************************************************
+ *                                                                   *
+ * Begin functions that verify data, or that adjust the verification *
+ * information (compute CRCs, rebuild headers)                       *
+ *                                                                   *
+ *********************************************************************/
+
+// Perform detailed verification, reporting on any problems found, but
+// do *NOT* recover from these problems. Returns the total number of
+// problems identified.
+int GPTData::Verify(void) {
+   int problems = 0, alignProbs = 0;
+   uint32_t i, numSegments;
+   uint64_t totalFree, largestSegment;
+
+   // First, check for CRC errors in the GPT data....
+   if (!mainCrcOk) {
+      problems++;
+      cout << "\nProblem: The CRC for the main GPT header is invalid. The main GPT header may\n"
+           << "be corrupt. Consider loading the backup GPT header to rebuild the main GPT\n"
+           << "header ('b' on the recovery & transformation menu). This report may be a false\n"
+           << "alarm if you've already corrected other problems.\n";
+   } // if
+   if (!mainPartsCrcOk) {
+      problems++;
+      cout << "\nProblem: The CRC for the main partition table is invalid. This table may be\n"
+           << "corrupt. Consider loading the backup partition table ('c' on the recovery &\n"
+           << "transformation menu). This report may be a false alarm if you've already\n"
+           << "corrected other problems.\n";
+   } // if
+   if (!secondCrcOk) {
+      problems++;
+      cout << "\nProblem: The CRC for the backup GPT header is invalid. The backup GPT header\n"
+           << "may be corrupt. Consider using the main GPT header to rebuild the backup GPT\n"
+           << "header ('d' on the recovery & transformation menu). This report may be a false\n"
+           << "alarm if you've already corrected other problems.\n";
+   } // if
+   if (!secondPartsCrcOk) {
+      problems++;
+      cout << "\nCaution: The CRC for the backup partition table is invalid. This table may\n"
+           << "be corrupt. This program will automatically create a new backup partition\n"
+           << "table when you save your partitions.\n";
+   } // if
+
+   // Now check that the main and backup headers both point to themselves....
+   if (mainHeader.currentLBA != 1) {
+      problems++;
+      cout << "\nProblem: The main header's self-pointer doesn't point to itself. This problem\n"
+           << "is being automatically corrected, but it may be a symptom of more serious\n"
+           << "problems. Think carefully before saving changes with 'w' or using this disk.\n";
+      mainHeader.currentLBA = 1;
+   } // if
+   if (secondHeader.currentLBA != (diskSize - UINT64_C(1))) {
+      problems++;
+      cout << "\nProblem: The secondary header's self-pointer indicates that it doesn't reside\n"
+           << "at the end of the disk. If you've added a disk to a RAID array, use the 'e'\n"
+           << "option on the experts' menu to adjust the secondary header's and partition\n"
+           << "table's locations.\n";
+   } // if
+
+   // Now check that critical main and backup GPT entries match each other
+   if (mainHeader.currentLBA != secondHeader.backupLBA) {
+      problems++;
+      cout << "\nProblem: main GPT header's current LBA pointer (" << mainHeader.currentLBA
+           << ") doesn't\nmatch the backup GPT header's alternate LBA pointer("
+           << secondHeader.backupLBA << ").\n";
+   } // if
+   if (mainHeader.backupLBA != secondHeader.currentLBA) {
+      problems++;
+      cout << "\nProblem: main GPT header's backup LBA pointer (" << mainHeader.backupLBA
+           << ") doesn't\nmatch the backup GPT header's current LBA pointer ("
+           << secondHeader.currentLBA << ").\n"
+           << "The 'e' option on the experts' menu may fix this problem.\n";
+   } // if
+   if (mainHeader.firstUsableLBA != secondHeader.firstUsableLBA) {
+      problems++;
+      cout << "\nProblem: main GPT header's first usable LBA pointer (" << mainHeader.firstUsableLBA
+           << ") doesn't\nmatch the backup GPT header's first usable LBA pointer ("
+           << secondHeader.firstUsableLBA << ")\n";
+   } // if
+   if (mainHeader.lastUsableLBA != secondHeader.lastUsableLBA) {
+      problems++;
+      cout << "\nProblem: main GPT header's last usable LBA pointer (" << mainHeader.lastUsableLBA
+           << ") doesn't\nmatch the backup GPT header's last usable LBA pointer ("
+           << secondHeader.lastUsableLBA << ")\n"
+           << "The 'e' option on the experts' menu can probably fix this problem.\n";
+   } // if
+   if ((mainHeader.diskGUID != secondHeader.diskGUID)) {
+      problems++;
+      cout << "\nProblem: main header's disk GUID (" << mainHeader.diskGUID
+           << ") doesn't\nmatch the backup GPT header's disk GUID ("
+           << secondHeader.diskGUID << ")\n"
+           << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
+           << "select one or the other header.\n";
+   } // if
+   if (mainHeader.numParts != secondHeader.numParts) {
+      problems++;
+      cout << "\nProblem: main GPT header's number of partitions (" << mainHeader.numParts
+           << ") doesn't\nmatch the backup GPT header's number of partitions ("
+           << secondHeader.numParts << ")\n"
+           << "Resizing the partition table ('s' on the experts' menu) may help.\n";
+   } // if
+   if (mainHeader.sizeOfPartitionEntries != secondHeader.sizeOfPartitionEntries) {
+      problems++;
+      cout << "\nProblem: main GPT header's size of partition entries ("
+           << mainHeader.sizeOfPartitionEntries << ") doesn't\n"
+           << "match the backup GPT header's size of partition entries ("
+           << secondHeader.sizeOfPartitionEntries << ")\n"
+           << "You should use the 'b' or 'd' option on the recovery & transformation menu to\n"
+           << "select one or the other header.\n";
+   } // if
+
+   // Now check for a few other miscellaneous problems...
+   // Check that the disk size will hold the data...
+   if (mainHeader.backupLBA >= diskSize) {
+      problems++;
+      cout << "\nProblem: Disk is too small to hold all the data!\n"
+           << "(Disk size is " << diskSize << " sectors, needs to be "
+           << mainHeader.backupLBA + UINT64_C(1) << " sectors.)\n"
+           << "The 'e' option on the experts' menu may fix this problem.\n";
+   } // if
+
+   if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) {
+      problems++;
+      cout << "\nProblem: GPT claims the disk is larger than it is! (Claimed last usable\n"
+           << "sector is " << mainHeader.lastUsableLBA << ", but backup header is at\n"
+           << mainHeader.backupLBA << " and disk size is " << diskSize << " sectors.\n"
+           << "The 'e' option on the experts' menu will probably fix this problem\n";
+   }
+
+   // 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();
+
+   // Check for MBR-specific problems....
+   problems += VerifyMBR();
+
+   // Check for a 0xEE protective partition that's marked as active....
+   if (protectiveMBR.IsEEActive()) {
+      cout << "\nWarning: The 0xEE protective partition in the MBR is marked as active. This is\n"
+           << "technically a violation of the GPT specification, and can cause some EFIs to\n"
+           << "ignore the disk, but it is required to boot from a GPT disk on some BIOS-based\n"
+           << "computers. You can clear this flag by creating a fresh protective MBR using\n"
+           << "the 'n' option on the experts' menu.\n";
+   }
+
+   // Verify that partitions don't run into GPT data areas....
+   problems += CheckGPTSize();
+
+   if (!protectiveMBR.DoTheyFit()) {
+      cout << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n"
+           << "fresh protective or hybrid MBR is recommended.\n";
+      problems++;
+   }
+
+   // Check that partitions are aligned on proper boundaries (for WD Advanced
+   // Format and similar disks)....
+   for (i = 0; i < numParts; i++) {
+      if ((partitions[i].IsUsed()) && (partitions[i].GetFirstLBA() % sectorAlignment) != 0) {
+         cout << "\nCaution: Partition " << i + 1 << " doesn't begin on a "
+              << sectorAlignment << "-sector boundary. This may\nresult "
+              << "in degraded performance on some modern (2009 and later) hard disks.\n";
+         alignProbs++;
+      } // if
+   } // for
+   if (alignProbs > 0)
+      cout << "\nConsult http://www.ibm.com/developerworks/linux/library/l-4kb-sector-disks/\n"
+      << "for information on disk alignment.\n";
+
+   // Now compute available space, but only if no problems found, since
+   // problems could affect the results
+   if (problems == 0) {
+      totalFree = FindFreeBlocks(&numSegments, &largestSegment);
+      cout << "\nNo problems found. " << totalFree << " free sectors ("
+           << BytesToIeee(totalFree, blockSize) << ") available in "
+           << numSegments << "\nsegments, the largest of which is "
+           << largestSegment << " (" << BytesToIeee(largestSegment, blockSize)
+           << ") in size.\n";
+   } else {
+      cout << "\nIdentified " << problems << " problems!\n";
+   } // if/else
+
+   return (problems);
+} // GPTData::Verify()
+
+// Checks to see if the GPT tables overrun existing partitions; if they
+// do, issues a warning but takes no action. Returns number of problems
+// detected (0 if OK, 1 to 2 if problems).
+int GPTData::CheckGPTSize(void) {
+   uint64_t overlap, firstUsedBlock, lastUsedBlock;
+   uint32_t i;
+   int numProbs = 0;
+
+   // first, locate the first & last used blocks
+   firstUsedBlock = UINT64_MAX;
+   lastUsedBlock = 0;
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) {
+         if (partitions[i].GetFirstLBA() < firstUsedBlock)
+            firstUsedBlock = partitions[i].GetFirstLBA();
+         if (partitions[i].GetLastLBA() > lastUsedBlock) {
+            lastUsedBlock = partitions[i].GetLastLBA();
+         } // if
+      } // if
+   } // for
+
+   // If the disk size is 0 (the default), then it means that various
+   // variables aren't yet set, so the below tests will be useless;
+   // therefore we should skip everything
+   if (diskSize != 0) {
+      if (mainHeader.firstUsableLBA > firstUsedBlock) {
+         overlap = mainHeader.firstUsableLBA - firstUsedBlock;
+         cout << "Warning! Main partition table overlaps the first partition by "
+              << overlap << " blocks!\n";
+         if (firstUsedBlock > 2) {
+            cout << "Try reducing the partition table size by " << overlap * 4
+                 << " entries.\n(Use the 's' item on the experts' menu.)\n";
+         } else {
+            cout << "You will need to delete this partition or resize it in another utility.\n";
+         } // if/else
+         numProbs++;
+      } // Problem at start of disk
+      if (mainHeader.lastUsableLBA < lastUsedBlock) {
+         overlap = lastUsedBlock - mainHeader.lastUsableLBA;
+         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";
+         } else {
+            cout << "Try reducing the partition table size by " << overlap * 4
+                 << " entries.\n(Use the 's' item on the experts' menu.)\n";
+         } // if/else
+         numProbs++;
+      } // Problem at end of disk
+   } // if (diskSize != 0)
+   return numProbs;
+} // GPTData::CheckGPTSize()
+
+// Check the validity of the GPT header. Returns 1 if the main header
+// is valid, 2 if the backup header is valid, 3 if both are valid, and
+// 0 if neither is valid. Note that this function checks the GPT signature,
+// revision value, and CRCs in both headers.
+int GPTData::CheckHeaderValidity(void) {
+   int valid = 3;
+
+   cout.setf(ios::uppercase);
+   cout.fill('0');
+
+   // Note: failed GPT signature checks produce no error message because
+   // a message is displayed in the ReversePartitionBytes() function
+   if ((mainHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&mainHeader, 1))) {
+      valid -= 1;
+   } else if ((mainHeader.revision != 0x00010000) && valid) {
+      valid -= 1;
+      cout << "Unsupported GPT version in main header; read 0x";
+      cout.width(8);
+      cout << hex << mainHeader.revision << ", should be\n0x";
+      cout.width(8);
+      cout << UINT32_C(0x00010000) << dec << "\n";
+   } // if/else/if
+
+   if ((secondHeader.signature != GPT_SIGNATURE) || (!CheckHeaderCRC(&secondHeader))) {
+      valid -= 2;
+   } else if ((secondHeader.revision != 0x00010000) && valid) {
+      valid -= 2;
+      cout << "Unsupported GPT version in backup header; read 0x";
+      cout.width(8);
+      cout << hex << secondHeader.revision << ", should be\n0x";
+      cout.width(8);
+      cout << UINT32_C(0x00010000) << dec << "\n";
+   } // if/else/if
+
+   // Check for an Apple disk signature
+   if (((mainHeader.signature << 32) == APM_SIGNATURE1) ||
+        (mainHeader.signature << 32) == APM_SIGNATURE2) {
+      apmFound = 1; // Will display warning message later
+   } // if
+   cout.fill(' ');
+
+   return valid;
+} // GPTData::CheckHeaderValidity()
+
+// Check the header CRC to see if it's OK...
+// Note: Must be called with header in platform-ordered byte order.
+// Returns 1 if header's computed CRC matches the stored value, 0 if the
+// computed and stored values don't match
+int GPTData::CheckHeaderCRC(struct GPTHeader* header, int warn) {
+   uint32_t oldCRC, newCRC, hSize;
+   uint8_t *temp;
+
+   // Back up old header CRC and then blank it, since it must be 0 for
+   // computation to be valid
+   oldCRC = header->headerCRC;
+   header->headerCRC = UINT32_C(0);
+
+   hSize = header->headerSize;
+
+   if (IsLittleEndian() == 0)
+      ReverseHeaderBytes(header);
+
+   if ((hSize > blockSize) || (hSize < HEADER_SIZE)) {
+      if (warn) {
+         cerr << "\aWarning! Header size is specified as " << hSize << ", which is invalid.\n";
+         cerr << "Setting the header size for CRC computation to " << HEADER_SIZE << "\n";
+      } // if
+      hSize = HEADER_SIZE;
+   } else if ((hSize > sizeof(GPTHeader)) && warn) {
+      cout << "\aCaution! Header size for CRC check is " << hSize << ", which is greater than " << sizeof(GPTHeader) << ".\n";
+      cout << "If stray data exists after the header on the header sector, it will be ignored,\n"
+           << "which may result in a CRC false alarm.\n";
+   } // if/elseif
+   temp = new uint8_t[hSize];
+   if (temp != NULL) {
+      memset(temp, 0, hSize);
+      if (hSize < sizeof(GPTHeader))
+         memcpy(temp, header, hSize);
+      else
+         memcpy(temp, header, sizeof(GPTHeader));
+
+      newCRC = chksum_crc32((unsigned char*) temp, hSize);
+      delete[] temp;
+   } else {
+      cerr << "Could not allocate memory in GPTData::CheckHeaderCRC()! Aborting!\n";
+      exit(1);
+   }
+   if (IsLittleEndian() == 0)
+      ReverseHeaderBytes(header);
+   header->headerCRC = oldCRC;
+   return (oldCRC == newCRC);
+} // GPTData::CheckHeaderCRC()
+
+// Recompute all the CRCs. Must be called before saving if any changes have
+// been made. Must be called on platform-ordered data (this function reverses
+// byte order and then undoes that reversal.)
+void GPTData::RecomputeCRCs(void) {
+   uint32_t crc, hSize;
+   int littleEndian = 1;
+
+   // If the header size is bigger than the GPT header data structure, reset it;
+   // otherwise, set both header sizes to whatever the main one is....
+   if (mainHeader.headerSize > sizeof(GPTHeader))
+      hSize = secondHeader.headerSize = mainHeader.headerSize = HEADER_SIZE;
+   else
+      hSize = secondHeader.headerSize = mainHeader.headerSize;
+
+   if ((littleEndian = IsLittleEndian()) == 0) {
+      ReversePartitionBytes();
+      ReverseHeaderBytes(&mainHeader);
+      ReverseHeaderBytes(&secondHeader);
+   } // if
+
+   // Compute CRC of partition tables & store in main and secondary headers
+   crc = chksum_crc32((unsigned char*) partitions, numParts * GPT_SIZE);
+   mainHeader.partitionEntriesCRC = crc;
+   secondHeader.partitionEntriesCRC = crc;
+   if (littleEndian == 0) {
+      ReverseBytes(&mainHeader.partitionEntriesCRC, 4);
+      ReverseBytes(&secondHeader.partitionEntriesCRC, 4);
+   } // if
+
+   // Zero out GPT headers' own CRCs (required for correct computation)
+   mainHeader.headerCRC = 0;
+   secondHeader.headerCRC = 0;
+
+   crc = chksum_crc32((unsigned char*) &mainHeader, hSize);
+   if (littleEndian == 0)
+      ReverseBytes(&crc, 4);
+   mainHeader.headerCRC = crc;
+   crc = chksum_crc32((unsigned char*) &secondHeader, hSize);
+   if (littleEndian == 0)
+      ReverseBytes(&crc, 4);
+   secondHeader.headerCRC = crc;
+
+   if (littleEndian == 0) {
+      ReverseHeaderBytes(&mainHeader);
+      ReverseHeaderBytes(&secondHeader);
+      ReversePartitionBytes();
+   } // if
+} // GPTData::RecomputeCRCs()
+
+// Rebuild the main GPT header, using the secondary header as a model.
+// Typically called when the main header has been found to be corrupt.
+void GPTData::RebuildMainHeader(void) {
+   mainHeader.signature = GPT_SIGNATURE;
+   mainHeader.revision = secondHeader.revision;
+   mainHeader.headerSize = secondHeader.headerSize;
+   mainHeader.headerCRC = UINT32_C(0);
+   mainHeader.reserved = secondHeader.reserved;
+   mainHeader.currentLBA = secondHeader.backupLBA;
+   mainHeader.backupLBA = secondHeader.currentLBA;
+   mainHeader.firstUsableLBA = secondHeader.firstUsableLBA;
+   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA;
+   mainHeader.diskGUID = secondHeader.diskGUID;
+   mainHeader.partitionEntriesLBA = UINT64_C(2);
+   mainHeader.numParts = secondHeader.numParts;
+   mainHeader.sizeOfPartitionEntries = secondHeader.sizeOfPartitionEntries;
+   mainHeader.partitionEntriesCRC = secondHeader.partitionEntriesCRC;
+   memcpy(mainHeader.reserved2, secondHeader.reserved2, sizeof(mainHeader.reserved2));
+   mainCrcOk = secondCrcOk;
+   SetGPTSize(mainHeader.numParts, 0);
+} // GPTData::RebuildMainHeader()
+
+// Rebuild the secondary GPT header, using the main header as a model.
+void GPTData::RebuildSecondHeader(void) {
+   secondHeader.signature = GPT_SIGNATURE;
+   secondHeader.revision = mainHeader.revision;
+   secondHeader.headerSize = mainHeader.headerSize;
+   secondHeader.headerCRC = UINT32_C(0);
+   secondHeader.reserved = mainHeader.reserved;
+   secondHeader.currentLBA = mainHeader.backupLBA;
+   secondHeader.backupLBA = mainHeader.currentLBA;
+   secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
+   secondHeader.lastUsableLBA = mainHeader.lastUsableLBA;
+   secondHeader.diskGUID = mainHeader.diskGUID;
+   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+   secondHeader.numParts = mainHeader.numParts;
+   secondHeader.sizeOfPartitionEntries = mainHeader.sizeOfPartitionEntries;
+   secondHeader.partitionEntriesCRC = mainHeader.partitionEntriesCRC;
+   memcpy(secondHeader.reserved2, mainHeader.reserved2, sizeof(secondHeader.reserved2));
+   secondCrcOk = mainCrcOk;
+   SetGPTSize(secondHeader.numParts, 0);
+} // GPTData::RebuildSecondHeader()
+
+// Search for hybrid MBR entries that have no corresponding GPT partition.
+// Returns number of such mismatches found
+int GPTData::FindHybridMismatches(void) {
+   int i, found, numFound = 0;
+   uint32_t j;
+   uint64_t mbrFirst, mbrLast;
+
+   for (i = 0; i < 4; i++) {
+      if ((protectiveMBR.GetType(i) != 0xEE) && (protectiveMBR.GetType(i) != 0x00)) {
+         j = 0;
+         found = 0;
+         mbrFirst = (uint64_t) protectiveMBR.GetFirstSector(i);
+         mbrLast = mbrFirst + (uint64_t) protectiveMBR.GetLength(i) - UINT64_C(1);
+         do {
+            if ((j < numParts) && (partitions[j].GetFirstLBA() == mbrFirst) &&
+                (partitions[j].GetLastLBA() == mbrLast) && (partitions[j].IsUsed()))
+               found = 1;
+            j++;
+         } while ((!found) && (j < numParts));
+         if (!found) {
+            numFound++;
+            cout << "\nWarning! Mismatched GPT and MBR partition! MBR partition "
+                 << i + 1 << ", of type 0x";
+            cout.fill('0');
+            cout.setf(ios::uppercase);
+            cout.width(2);
+            cout << hex << (int) protectiveMBR.GetType(i) << ",\n"
+                 << "has no corresponding GPT partition! You may continue, but this condition\n"
+                 << "might cause data loss in the future!\a\n" << dec;
+            cout.fill(' ');
+         } // if
+      } // if
+   } // for
+   return numFound;
+} // GPTData::FindHybridMismatches
+
+// Find overlapping partitions and warn user about them. Returns number of
+// overlapping partitions.
+// Returns number of overlapping segments found.
+int GPTData::FindOverlaps(void) {
+   int problems = 0;
+   uint32_t i, j;
+
+   for (i = 1; i < numParts; i++) {
+      for (j = 0; j < i; j++) {
+         if ((partitions[i].IsUsed()) && (partitions[j].IsUsed()) &&
+             (partitions[i].DoTheyOverlap(partitions[j]))) {
+            problems++;
+            cout << "\nProblem: partitions " << i + 1 << " and " << j + 1 << " overlap:\n";
+            cout << "  Partition " << i + 1 << ": " << partitions[i].GetFirstLBA()
+                 << " to " << partitions[i].GetLastLBA() << "\n";
+            cout << "  Partition " << j + 1 << ": " << partitions[j].GetFirstLBA()
+                 << " to " << partitions[j].GetLastLBA() << "\n";
+         } // if
+      } // for j...
+   } // for i...
+   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....)
+// Returns number of problems found.
+int GPTData::FindInsanePartitions(void) {
+   uint32_t i;
+   int problems = 0;
+
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) {
+         if (partitions[i].GetFirstLBA() > partitions[i].GetLastLBA()) {
+            problems++;
+            cout << "\nProblem: partition " << i + 1 << " ends before it begins.\n";
+         } // if
+         if (partitions[i].GetLastLBA() >= diskSize) {
+            problems++;
+         cout << "\nProblem: partition " << i + 1 << " is too big for the disk.\n";
+         } // if
+      } // if
+   } // for
+   return problems;
+} // GPTData::FindInsanePartitions(void)
+
+
+/******************************************************************
+ *                                                                *
+ * Begin functions that load data from disk or save data to disk. *
+ *                                                                *
+ ******************************************************************/
+
+// Change the filename associated with the GPT. Used for duplicating
+// the partition table to a new disk and saving backups.
+// Returns 1 on success, 0 on failure.
+int GPTData::SetDisk(const string & deviceFilename) {
+   int err, allOK = 1;
+
+   device = deviceFilename;
+   if (allOK && myDisk.OpenForRead(deviceFilename)) {
+      // store disk information....
+      diskSize = myDisk.DiskSize(&err);
+      blockSize = (uint32_t) myDisk.GetBlockSize();
+   } // if
+   protectiveMBR.SetDisk(&myDisk);
+   protectiveMBR.SetDiskSize(diskSize);
+   protectiveMBR.SetBlockSize(blockSize);
+   return allOK;
+} // GPTData::SetDisk()
+
+// Scan for partition data. This function loads the MBR data (regular MBR or
+// protective MBR) and loads BSD disklabel data (which is probably invalid).
+// It also looks for APM data, forces a load of GPT data, and summarizes
+// the results.
+void GPTData::PartitionScan(void) {
+   BSDData bsdDisklabel;
+
+   // Read the MBR & check for BSD disklabel
+   protectiveMBR.ReadMBRData(&myDisk);
+   bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
+
+   // Load the GPT data, whether or not it's valid
+   ForceLoadGPTData();
+
+   // Some tools create a 0xEE partition that's too big. If this is detected,
+   // normalize it....
+   if ((state == gpt_valid) && !protectiveMBR.DoTheyFit() && (protectiveMBR.GetValidity() == gpt)) {
+      if (!beQuiet) {
+         cerr << "\aThe protective MBR's 0xEE partition is oversized! Auto-repairing.\n\n";
+      } // if
+      protectiveMBR.MakeProtectiveMBR();
+   } // if
+
+   if (!beQuiet) {
+      cout << "Partition table scan:\n";
+      protectiveMBR.ShowState();
+      bsdDisklabel.ShowState();
+      ShowAPMState(); // Show whether there's an Apple Partition Map present
+      ShowGPTState(); // Show GPT status
+      cout << "\n";
+   } // if
+
+   if (apmFound) {
+      cout << "\n*******************************************************************\n"
+           << "This disk appears to contain an Apple-format (APM) partition table!\n";
+      if (!justLooking) {
+         cout << "It will be destroyed if you continue!\n";
+      } // if
+      cout << "*******************************************************************\n\n\a";
+   } // if
+} // GPTData::PartitionScan()
+
+// Read GPT data from a disk.
+int GPTData::LoadPartitions(const string & deviceFilename) {
+   BSDData bsdDisklabel;
+   int err, allOK = 1;
+   MBRValidity mbrState;
+
+   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
+#if defined (__APPLE__)
+         cout << "You may need to deactivate System Integrity Protection to use this program. See\n"
+              << "https://www.quora.com/How-do-I-turn-off-the-rootless-in-OS-X-El-Capitan-10-11\n"
+              << "for more information.\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();
+      device = deviceFilename;
+      PartitionScan(); // Check for partition types, load GPT, & print summary
+
+      whichWasUsed = UseWhichPartitions();
+      switch (whichWasUsed) {
+         case use_mbr:
+            XFormPartitions();
+            break;
+         case use_bsd:
+            bsdDisklabel.ReadBSDData(&myDisk, 0, diskSize - 1);
+//            bsdDisklabel.DisplayBSDData();
+            ClearGPTData();
+            protectiveMBR.MakeProtectiveMBR(1); // clear boot area (option 1)
+            XFormDisklabel(&bsdDisklabel);
+            break;
+         case use_gpt:
+            mbrState = protectiveMBR.GetValidity();
+            if ((mbrState == invalid) || (mbrState == mbr))
+               protectiveMBR.MakeProtectiveMBR();
+            break;
+         case use_new:
+            ClearGPTData();
+            protectiveMBR.MakeProtectiveMBR();
+            break;
+         case use_abort:
+            allOK = 0;
+            cerr << "Invalid partition data!\n";
+            break;
+      } // switch
+
+      if (allOK)
+         CheckGPTSize();
+      myDisk.Close();
+      ComputeAlignment();
+   } else {
+      allOK = 0;
+   } // if/else
+   return (allOK);
+} // GPTData::LoadPartitions()
+
+// Loads the GPT, as much as possible. Returns 1 if this seems to have
+// succeeded, 0 if there are obvious problems....
+int GPTData::ForceLoadGPTData(void) {
+   int allOK, validHeaders, loadedTable = 1;
+
+   allOK = LoadHeader(&mainHeader, myDisk, 1, &mainCrcOk);
+
+   if (mainCrcOk && (mainHeader.backupLBA < diskSize)) {
+      allOK = LoadHeader(&secondHeader, myDisk, mainHeader.backupLBA, &secondCrcOk) && allOK;
+   } else {
+      allOK = LoadHeader(&secondHeader, myDisk, diskSize - UINT64_C(1), &secondCrcOk) && allOK;
+      if (mainCrcOk && (mainHeader.backupLBA >= diskSize))
+         cout << "Warning! Disk size is smaller than the main header indicates! Loading\n"
+              << "secondary header from the last sector of the disk! You should use 'v' to\n"
+              << "verify disk integrity, and perhaps options on the experts' menu to repair\n"
+              << "the disk.\n";
+   } // if/else
+   if (!allOK)
+      state = gpt_invalid;
+
+   // Return valid headers code: 0 = both headers bad; 1 = main header
+   // good, backup bad; 2 = backup header good, main header bad;
+   // 3 = both headers good. Note these codes refer to valid GPT
+   // signatures, version numbers, and CRCs.
+   validHeaders = CheckHeaderValidity();
+
+   // Read partitions (from primary array)
+   if (validHeaders > 0) { // if at least one header is OK....
+      // GPT appears to be valid....
+      state = gpt_valid;
+
+      // We're calling the GPT valid, but there's a possibility that one
+      // of the two headers is corrupt. If so, use the one that seems to
+      // be in better shape to regenerate the bad one
+      if (validHeaders == 1) { // valid main header, invalid backup header
+         cerr << "\aCaution: invalid backup GPT header, but valid main header; regenerating\n"
+              << "backup header from main header.\n\n";
+         RebuildSecondHeader();
+         state = gpt_corrupt;
+         secondCrcOk = mainCrcOk; // Since regenerated, use CRC validity of main
+      } else if (validHeaders == 2) { // valid backup header, invalid main header
+         cerr << "\aCaution: invalid main GPT header, but valid backup; regenerating main header\n"
+              << "from backup!\n\n";
+         RebuildMainHeader();
+         state = gpt_corrupt;
+         mainCrcOk = secondCrcOk; // Since copied, use CRC validity of backup
+      } // if/else/if
+
+      // Figure out which partition table to load....
+      // Load the main partition table, since either its header's CRC is OK or the
+      // backup header's CRC is not OK....
+      if (mainCrcOk || !secondCrcOk) {
+         if (LoadMainTable() == 0)
+            allOK = 0;
+      } else { // bad main header CRC and backup header CRC is OK
+         state = gpt_corrupt;
+         if (LoadSecondTableAsMain()) {
+            loadedTable = 2;
+            cerr << "\aWarning: Invalid CRC on main header data; loaded backup partition table.\n";
+         } else { // backup table bad, bad main header CRC, but try main table in desperation....
+            if (LoadMainTable() == 0) {
+               allOK = 0;
+               loadedTable = 0;
+               cerr << "\a\aWarning! Unable to load either main or backup partition table!\n";
+            } // if
+         } // if/else (LoadSecondTableAsMain())
+      } // if/else (load partition table)
+
+      if (loadedTable == 1)
+         secondPartsCrcOk = CheckTable(&secondHeader);
+      else if (loadedTable == 2)
+         mainPartsCrcOk = CheckTable(&mainHeader);
+      else
+         mainPartsCrcOk = secondPartsCrcOk = 0;
+
+      // Problem with main partition table; if backup is OK, use it instead....
+      if (secondPartsCrcOk && secondCrcOk && !mainPartsCrcOk) {
+         state = gpt_corrupt;
+         allOK = allOK && LoadSecondTableAsMain();
+         mainPartsCrcOk = 0; // LoadSecondTableAsMain() resets this, so re-flag as bad
+         cerr << "\aWarning! Main partition table CRC mismatch! Loaded backup "
+              << "partition table\ninstead of main partition table!\n\n";
+      } // if */
+
+      // Check for valid CRCs and warn if there are problems
+      if ((mainCrcOk == 0) || (secondCrcOk == 0) || (mainPartsCrcOk == 0) ||
+           (secondPartsCrcOk == 0)) {
+         cerr << "Warning! One or more CRCs don't match. You should repair the disk!\n\n";
+         state = gpt_corrupt;
+      } // if
+   } else {
+      state = gpt_invalid;
+   } // if/else
+   return allOK;
+} // GPTData::ForceLoadGPTData()
+
+// Loads the partition table pointed to by the main GPT header. The
+// main GPT header in memory MUST be valid for this call to do anything
+// sensible!
+// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
+int GPTData::LoadMainTable(void) {
+   return LoadPartitionTable(mainHeader, myDisk);
+} // GPTData::LoadMainTable()
+
+// Load the second (backup) partition table as the primary partition
+// table. Used in repair functions, and when starting up if the main
+// partition table is damaged.
+// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
+int GPTData::LoadSecondTableAsMain(void) {
+   return LoadPartitionTable(secondHeader, myDisk);
+} // GPTData::LoadSecondTableAsMain()
+
+// Load a single GPT header (main or backup) from the specified disk device and
+// sector. Applies byte-order corrections on big-endian platforms. Sets crcOk
+// value appropriately.
+// Returns 1 on success, 0 on failure. Note that CRC errors do NOT qualify as
+// failure.
+int GPTData::LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk) {
+   int allOK = 1;
+   GPTHeader tempHeader;
+
+   disk.Seek(sector);
+   if (disk.Read(&tempHeader, 512) != 512) {
+      cerr << "Warning! Read error " << errno << "; strange behavior now likely!\n";
+      allOK = 0;
+   } // if
+
+   // Reverse byte order, if necessary
+   if (IsLittleEndian() == 0) {
+      ReverseHeaderBytes(&tempHeader);
+   } // if
+   *crcOk = CheckHeaderCRC(&tempHeader);
+
+   if (allOK && (numParts != tempHeader.numParts) && *crcOk) {
+      allOK = SetGPTSize(tempHeader.numParts, 0);
+   }
+
+   *header = tempHeader;
+   return allOK;
+} // GPTData::LoadHeader
+
+// Load a partition table (either main or secondary) from the specified disk,
+// using header as a reference for what to load. If sector != 0 (the default
+// is 0), loads from the specified sector; otherwise loads from the sector
+// indicated in header.
+// Returns 1 on success, 0 on failure. CRC errors do NOT count as failure.
+int GPTData::LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector) {
+   uint32_t sizeOfParts, newCRC;
+   int retval;
+
+   if (disk.OpenForRead()) {
+      if (sector == 0) {
+         retval = disk.Seek(header.partitionEntriesLBA);
+      } else {
+         retval = disk.Seek(sector);
+      } // if/else
+      if (retval == 1)
+         retval = SetGPTSize(header.numParts, 0);
+      if (retval == 1) {
+         sizeOfParts = header.numParts * header.sizeOfPartitionEntries;
+         if (disk.Read(partitions, sizeOfParts) != (int) sizeOfParts) {
+            cerr << "Warning! Read error " << errno << "! Misbehavior now likely!\n";
+            retval = 0;
+         } // if
+         newCRC = chksum_crc32((unsigned char*) partitions, sizeOfParts);
+         mainPartsCrcOk = secondPartsCrcOk = (newCRC == header.partitionEntriesCRC);
+         if (IsLittleEndian() == 0)
+            ReversePartitionBytes();
+         if (!mainPartsCrcOk) {
+            cout << "Caution! After loading partitions, the CRC doesn't check out!\n";
+         } // if
+      } else {
+         cerr << "Error! Couldn't seek to partition table!\n";
+      } // if/else
+   } else {
+      cerr << "Error! Couldn't open device " << device
+           << " when reading partition table!\n";
+      retval = 0;
+   } // if/else
+   return retval;
+} // GPTData::LoadPartitionsTable()
+
+// Check the partition table pointed to by header, but don't keep it
+// around.
+// Returns 1 if the CRC is OK & this table matches the one already in memory,
+// 0 if not or if there was a read error.
+int GPTData::CheckTable(struct GPTHeader *header) {
+   uint32_t sizeOfParts, newCRC;
+   GPTPart *partsToCheck;
+   GPTHeader *otherHeader;
+   int allOK = 0;
+
+   // Load partition table into temporary storage to check
+   // its CRC and store the results, then discard this temporary
+   // storage, since we don't use it in any but recovery operations
+   if (myDisk.Seek(header->partitionEntriesLBA)) {
+      partsToCheck = new GPTPart[header->numParts];
+      sizeOfParts = header->numParts * header->sizeOfPartitionEntries;
+      if (partsToCheck == NULL) {
+         cerr << "Could not allocate memory in GPTData::CheckTable()! Terminating!\n";
+         exit(1);
+      } // if
+      if (myDisk.Read(partsToCheck, sizeOfParts) != (int) sizeOfParts) {
+         cerr << "Warning! Error " << errno << " reading partition table for CRC check!\n";
+      } else {
+         newCRC = chksum_crc32((unsigned char*) partsToCheck, sizeOfParts);
+         allOK = (newCRC == header->partitionEntriesCRC);
+         if (header == &mainHeader)
+            otherHeader = &secondHeader;
+         else
+            otherHeader = &mainHeader;
+         if (newCRC != otherHeader->partitionEntriesCRC) {
+            cerr << "Warning! Main and backup partition tables differ! Use the 'c' and 'e' options\n"
+                 << "on the recovery & transformation menu to examine the two tables.\n\n";
+            allOK = 0;
+         } // if
+      } // if/else
+      delete[] partsToCheck;
+   } // if
+   return allOK;
+} // GPTData::CheckTable()
+
+// Writes GPT (and protective MBR) to disk. If quiet==1, moves the second
+// header later on the disk without asking for permission, if necessary, and
+// doesn't confirm the operation before writing. If quiet==0, asks permission
+// before moving the second header and asks for final confirmation of any
+// write.
+// Returns 1 on successful write, 0 if there was a problem.
+int GPTData::SaveGPTData(int quiet) {
+   int allOK = 1, syncIt = 1;
+   char answer;
+
+   // First do some final sanity checks....
+
+   // This test should only fail on read-only disks....
+   if (justLooking) {
+      cout << "The justLooking flag is set. This probably means you can't write to the disk.\n";
+      allOK = 0;
+   } // if
+
+   // Check that disk is really big enough to handle the second header...
+   if (mainHeader.backupLBA >= diskSize) {
+      cerr << "Caution! Secondary header was placed beyond the disk's limits! Moving the\n"
+           << "header, but other problems may occur!\n";
+      MoveSecondHeaderToEnd();
+   } // if
+
+   // Is there enough space to hold the GPT headers and partition tables,
+   // given the partition sizes?
+   if (CheckGPTSize() > 0) {
+      allOK = 0;
+   } // if
+
+   // Check that second header is properly placed. Warn and ask if this should
+   // be corrected if the test fails....
+   if (mainHeader.backupLBA < (diskSize - UINT64_C(1))) {
+      if (quiet == 0) {
+         cout << "Warning! Secondary header is placed too early on the disk! Do you want to\n"
+              << "correct this problem? ";
+         if (GetYN() == 'Y') {
+            MoveSecondHeaderToEnd();
+            cout << "Have moved second header and partition table to correct location.\n";
+         } else {
+            cout << "Have not corrected the problem. Strange problems may occur in the future!\n";
+         } // if correction requested
+      } else { // Go ahead and do correction automatically
+         MoveSecondHeaderToEnd();
+      } // if/else quiet
+   } // if
+
+   if ((mainHeader.lastUsableLBA >= diskSize) || (mainHeader.lastUsableLBA > mainHeader.backupLBA)) {
+      if (quiet == 0) {
+         cout << "Warning! The claimed last usable sector is incorrect! Do you want to correct\n"
+              << "this problem? ";
+         if (GetYN() == 'Y') {
+            MoveSecondHeaderToEnd();
+            cout << "Have adjusted the second header and last usable sector value.\n";
+         } else {
+            cout << "Have not corrected the problem. Strange problems may occur in the future!\n";
+         } // if correction requested
+      } else { // go ahead and do correction automatically
+         MoveSecondHeaderToEnd();
+      } // if/else quiet
+   } // if
+
+   // Check for overlapping or insane partitions....
+   if ((FindOverlaps() > 0) || (FindInsanePartitions() > 0)) {
+      allOK = 0;
+      cerr << "Aborting write operation!\n";
+   } // if
+
+   // Check that protective MBR fits, and warn if it doesn't....
+   if (!protectiveMBR.DoTheyFit()) {
+      cerr << "\nPartition(s) in the protective MBR are too big for the disk! Creating a\n"
+           << "fresh protective or hybrid MBR is recommended.\n";
+   }
+
+   // Check for mismatched MBR and GPT data, but let it pass if found
+   // (function displays warning message)
+   FindHybridMismatches();
+
+   RecomputeCRCs();
+
+   if ((allOK) && (!quiet)) {
+      cout << "\nFinal checks complete. About to write GPT data. THIS WILL OVERWRITE EXISTING\n"
+           << "PARTITIONS!!\n\nDo you want to proceed? ";
+      answer = GetYN();
+      if (answer == 'Y') {
+         cout << "OK; writing new GUID partition table (GPT) to " << myDisk.GetName() << ".\n";
+      } else {
+         allOK = 0;
+      } // if/else
+   } // if
+
+   // Do it!
+   if (allOK) {
+      if (myDisk.OpenForWrite()) {
+         // As per UEFI specs, write the secondary table and GPT first....
+         allOK = SavePartitionTable(myDisk, secondHeader.partitionEntriesLBA);
+         if (!allOK) {
+            cerr << "Unable to save backup partition table! Perhaps the 'e' option on the experts'\n"
+                 << "menu will resolve this problem.\n";
+            syncIt = 0;
+         } // if
+
+         // Now write the secondary GPT header...
+         allOK = allOK && SaveHeader(&secondHeader, myDisk, mainHeader.backupLBA);
+
+         // Now write the main partition tables...
+         allOK = allOK && SavePartitionTable(myDisk, mainHeader.partitionEntriesLBA);
+
+         // Now write the main GPT header...
+         allOK = allOK && SaveHeader(&mainHeader, myDisk, 1);
+
+         // To top it off, write the protective MBR...
+         allOK = allOK && protectiveMBR.WriteMBRData(&myDisk);
+
+         // re-read the partition table
+         // Note: Done even if some write operations failed, but not if all of them failed.
+         // Done this way because I've received one problem report from a user one whose
+         // system the MBR write failed but everything else was OK (on a GPT disk under
+         // Windows), and the failure to sync therefore caused Windows to restore the
+         // original partition table from its cache. OTOH, such restoration might be
+         // desirable if the error occurs later; but that seems unlikely unless the initial
+         // write fails....
+         if (syncIt)
+            myDisk.DiskSync();
+
+         if (allOK) { // writes completed OK
+            cout << "The operation has completed successfully.\n";
+         } else {
+            cerr << "Warning! An error was reported when writing the partition table! This error\n"
+                 << "MIGHT be harmless, or the disk might be damaged! Checking it is advisable.\n";
+         } // if/else
+
+         myDisk.Close();
+      } else {
+         cerr << "Unable to open device '" << myDisk.GetName() << "' for writing! Errno is "
+              << errno << "! Aborting write!\n";
+         allOK = 0;
+      } // if/else
+   } else {
+      cout << "Aborting write of new partition table.\n";
+   } // if
+
+   return (allOK);
+} // GPTData::SaveGPTData()
+
+// Save GPT data to a backup file. This function does much less error
+// checking than SaveGPTData(). It can therefore preserve many types of
+// corruption for later analysis; however, it preserves only the MBR,
+// the main GPT header, the backup GPT header, and the main partition
+// table; it discards the backup partition table, since it should be
+// identical to the main partition table on healthy disks.
+int GPTData::SaveGPTBackup(const string & filename) {
+   int allOK = 1;
+   DiskIO backupFile;
+
+   if (backupFile.OpenForWrite(filename)) {
+      // Recomputing the CRCs is likely to alter them, which could be bad
+      // if the intent is to save a potentially bad GPT for later analysis;
+      // but if we don't do this, we get bogus errors when we load the
+      // backup. I'm favoring misses over false alarms....
+      RecomputeCRCs();
+
+      protectiveMBR.WriteMBRData(&backupFile);
+      protectiveMBR.SetDisk(&myDisk);
+
+      if (allOK) {
+         // MBR write closed disk, so re-open and seek to end....
+         backupFile.OpenForWrite();
+         allOK = SaveHeader(&mainHeader, backupFile, 1);
+      } // if (allOK)
+
+      if (allOK)
+         allOK = SaveHeader(&secondHeader, backupFile, 2);
+
+      if (allOK)
+         allOK = SavePartitionTable(backupFile, 3);
+
+      if (allOK) { // writes completed OK
+         cout << "The operation has completed successfully.\n";
+      } else {
+         cerr << "Warning! An error was reported when writing the backup file.\n"
+              << "It may not be usable!\n";
+      } // if/else
+      backupFile.Close();
+   } else {
+      cerr << "Unable to open file '" << filename << "' for writing! Aborting!\n";
+      allOK = 0;
+   } // if/else
+   return allOK;
+} // GPTData::SaveGPTBackup()
+
+// Write a GPT header (main or backup) to the specified sector. Used by both
+// the SaveGPTData() and SaveGPTBackup() functions.
+// Should be passed an architecture-appropriate header (DO NOT call
+// ReverseHeaderBytes() on the header before calling this function)
+// Returns 1 on success, 0 on failure
+int GPTData::SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector) {
+   int littleEndian, allOK = 1;
+
+   littleEndian = IsLittleEndian();
+   if (!littleEndian)
+      ReverseHeaderBytes(header);
+   if (disk.Seek(sector)) {
+      if (disk.Write(header, 512) == -1)
+         allOK = 0;
+   } else allOK = 0; // if (disk.Seek()...)
+   if (!littleEndian)
+      ReverseHeaderBytes(header);
+   return allOK;
+} // GPTData::SaveHeader()
+
+// Save the partitions to the specified sector. Used by both the SaveGPTData()
+// and SaveGPTBackup() functions.
+// Should be passed an architecture-appropriate header (DO NOT call
+// ReverseHeaderBytes() on the header before calling this function)
+// Returns 1 on success, 0 on failure
+int GPTData::SavePartitionTable(DiskIO & disk, uint64_t sector) {
+   int littleEndian, allOK = 1;
+
+   littleEndian = IsLittleEndian();
+   if (disk.Seek(sector)) {
+      if (!littleEndian)
+         ReversePartitionBytes();
+      if (disk.Write(partitions, mainHeader.sizeOfPartitionEntries * numParts) == -1)
+         allOK = 0;
+      if (!littleEndian)
+         ReversePartitionBytes();
+   } else allOK = 0; // if (myDisk.Seek()...)
+   return allOK;
+} // GPTData::SavePartitionTable()
+
+// Load GPT data from a backup file created by SaveGPTBackup(). This function
+// does minimal error checking. It returns 1 if it completed successfully,
+// 0 if there was a problem. In the latter case, it creates a new empty
+// set of partitions.
+int GPTData::LoadGPTBackup(const string & filename) {
+   int allOK = 1, val, err;
+   int shortBackup = 0;
+   DiskIO backupFile;
+
+   if (backupFile.OpenForRead(filename)) {
+      // Let the MBRData class load the saved MBR...
+      protectiveMBR.ReadMBRData(&backupFile, 0); // 0 = don't check block size
+      protectiveMBR.SetDisk(&myDisk);
+
+      LoadHeader(&mainHeader, backupFile, 1, &mainCrcOk);
+
+      // Check backup file size and rebuild second header if file is right
+      // size to be direct dd copy of MBR, main header, and main partition
+      // table; if other size, treat it like a GPT fdisk-generated backup
+      // file
+      shortBackup = ((backupFile.DiskSize(&err) * backupFile.GetBlockSize()) ==
+                     (mainHeader.numParts * mainHeader.sizeOfPartitionEntries) + 1024);
+      if (shortBackup) {
+         RebuildSecondHeader();
+         secondCrcOk = mainCrcOk;
+      } else {
+         LoadHeader(&secondHeader, backupFile, 2, &secondCrcOk);
+      } // if/else
+
+      // Return valid headers code: 0 = both headers bad; 1 = main header
+      // good, backup bad; 2 = backup header good, main header bad;
+      // 3 = both headers good. Note these codes refer to valid GPT
+      // signatures and version numbers; more subtle problems will elude
+      // this check!
+      if ((val = CheckHeaderValidity()) > 0) {
+         if (val == 2) { // only backup header seems to be good
+            SetGPTSize(secondHeader.numParts, 0);
+         } else { // main header is OK
+            SetGPTSize(mainHeader.numParts, 0);
+         } // if/else
+
+         if (secondHeader.currentLBA != diskSize - UINT64_C(1)) {
+            cout << "Warning! Current disk size doesn't match that of the backup!\n"
+                 << "Adjusting sizes to match, but subsequent problems are possible!\n";
+            MoveSecondHeaderToEnd();
+         } // if
+
+         if (!LoadPartitionTable(mainHeader, backupFile, (uint64_t) (3 - shortBackup)))
+            cerr << "Warning! Read error " << errno
+                 << " loading partition table; strange behavior now likely!\n";
+      } else {
+         allOK = 0;
+      } // if/else
+      // Something went badly wrong, so blank out partitions
+      if (allOK == 0) {
+         cerr << "Improper backup file! Clearing all partition data!\n";
+         ClearGPTData();
+         protectiveMBR.MakeProtectiveMBR();
+      } // if
+   } else {
+      allOK = 0;
+      cerr << "Unable to open file '" << filename << "' for reading! Aborting!\n";
+   } // if/else
+
+   return allOK;
+} // GPTData::LoadGPTBackup()
+
+int GPTData::SaveMBR(void) {
+   return protectiveMBR.WriteMBRData(&myDisk);
+} // GPTData::SaveMBR()
+
+// This function destroys the on-disk GPT structures, but NOT the on-disk
+// MBR.
+// Returns 1 if the operation succeeds, 0 if not.
+int GPTData::DestroyGPT(void) {
+   int sum, tableSize, allOK = 1;
+   uint8_t blankSector[512];
+   uint8_t* emptyTable;
+
+   memset(blankSector, 0, sizeof(blankSector));
+   ClearGPTData();
+
+   if (myDisk.OpenForWrite()) {
+      if (!myDisk.Seek(mainHeader.currentLBA))
+         allOK = 0;
+      if (myDisk.Write(blankSector, 512) != 512) { // blank it out
+         cerr << "Warning! GPT main header not overwritten! Error is " << errno << "\n";
+         allOK = 0;
+      } // if
+      if (!myDisk.Seek(mainHeader.partitionEntriesLBA))
+         allOK = 0;
+      tableSize = numParts * mainHeader.sizeOfPartitionEntries;
+      emptyTable = new uint8_t[tableSize];
+      if (emptyTable == NULL) {
+         cerr << "Could not allocate memory in GPTData::DestroyGPT()! Terminating!\n";
+         exit(1);
+      } // if
+      memset(emptyTable, 0, tableSize);
+      if (allOK) {
+         sum = myDisk.Write(emptyTable, tableSize);
+         if (sum != tableSize) {
+            cerr << "Warning! GPT main partition table not overwritten! Error is " << errno << "\n";
+            allOK = 0;
+         } // if write failed
+      } // if 
+      if (!myDisk.Seek(secondHeader.partitionEntriesLBA))
+         allOK = 0;
+      if (allOK) {
+         sum = myDisk.Write(emptyTable, tableSize);
+         if (sum != tableSize) {
+            cerr << "Warning! GPT backup partition table not overwritten! Error is "
+                 << errno << "\n";
+            allOK = 0;
+         } // if wrong size written
+      } // if
+      if (!myDisk.Seek(secondHeader.currentLBA))
+         allOK = 0;
+      if (allOK) {
+         if (myDisk.Write(blankSector, 512) != 512) { // blank it out
+            cerr << "Warning! GPT backup header not overwritten! Error is " << errno << "\n";
+            allOK = 0;
+         } // if
+      } // if
+      myDisk.DiskSync();
+      myDisk.Close();
+      cout << "GPT data structures destroyed! You may now partition the disk using fdisk or\n"
+           << "other utilities.\n";
+      delete[] emptyTable;
+   } else {
+      cerr << "Problem opening '" << device << "' for writing! Program will now terminate.\n";
+   } // if/else (fd != -1)
+   return (allOK);
+} // GPTDataTextUI::DestroyGPT()
+
+// Wipe MBR data from the disk (zero it out completely)
+// Returns 1 on success, 0 on failure.
+int GPTData::DestroyMBR(void) {
+   int allOK;
+   uint8_t blankSector[512];
+
+   memset(blankSector, 0, sizeof(blankSector));
+
+   allOK = myDisk.OpenForWrite() && myDisk.Seek(0) && (myDisk.Write(blankSector, 512) == 512);
+
+   if (!allOK)
+      cerr << "Warning! MBR not overwritten! Error is " << errno << "!\n";
+   return allOK;
+} // GPTData::DestroyMBR(void)
+
+// Tell user whether Apple Partition Map (APM) was discovered....
+void GPTData::ShowAPMState(void) {
+   if (apmFound)
+      cout << "  APM: present\n";
+   else
+      cout << "  APM: not present\n";
+} // GPTData::ShowAPMState()
+
+// Tell user about the state of the GPT data....
+void GPTData::ShowGPTState(void) {
+   switch (state) {
+      case gpt_invalid:
+         cout << "  GPT: not present\n";
+         break;
+      case gpt_valid:
+         cout << "  GPT: present\n";
+         break;
+      case gpt_corrupt:
+         cout << "  GPT: damaged\n";
+         break;
+      default:
+         cout << "\a  GPT: unknown -- bug!\n";
+         break;
+   } // switch
+} // GPTData::ShowGPTState()
+
+// Display the basic GPT data
+void GPTData::DisplayGPTData(void) {
+   uint32_t i;
+   uint64_t temp, totalFree;
+
+   cout << "Disk " << device << ": " << diskSize << " sectors, "
+        << BytesToIeee(diskSize, blockSize) << "\n";
+   cout << "Logical sector size: " << blockSize << " bytes\n";
+   cout << "Disk identifier (GUID): " << mainHeader.diskGUID << "\n";
+   cout << "Partition table holds up to " << numParts << " entries\n";
+   cout << "First usable sector is " << mainHeader.firstUsableLBA
+        << ", last usable sector is " << mainHeader.lastUsableLBA << "\n";
+   totalFree = FindFreeBlocks(&i, &temp);
+   cout << "Partitions will be aligned on " << sectorAlignment << "-sector boundaries\n";
+   cout << "Total free space is " << totalFree << " sectors ("
+        << BytesToIeee(totalFree, blockSize) << ")\n";
+   cout << "\nNumber  Start (sector)    End (sector)  Size       Code  Name\n";
+   for (i = 0; i < numParts; i++) {
+      partitions[i].ShowSummary(i, blockSize);
+   } // for
+} // GPTData::DisplayGPTData()
+
+// Show detailed information on the specified partition
+void GPTData::ShowPartDetails(uint32_t partNum) {
+   if ((partNum < numParts) && !IsFreePartNum(partNum)) {
+      partitions[partNum].ShowDetails(blockSize);
+   } else {
+      cout << "Partition #" << partNum + 1 << " does not exist.\n";
+   } // if
+} // GPTData::ShowPartDetails()
+
+/**************************************************************************
+ *                                                                        *
+ * Partition table transformation functions (MBR or BSD disklabel to GPT) *
+ * (some of these functions may require user interaction)                 *
+ *                                                                        *
+ **************************************************************************/
+
+// Examines the MBR & GPT data to determine which set of data to use: the
+// MBR (use_mbr), the GPT (use_gpt), the BSD disklabel (use_bsd), or create
+// a new set of partitions (use_new). A return value of use_abort indicates
+// that this function couldn't determine what to do. Overriding functions
+// in derived classes may ask users questions in such cases.
+WhichToUse GPTData::UseWhichPartitions(void) {
+   WhichToUse which = use_new;
+   MBRValidity mbrState;
+
+   mbrState = protectiveMBR.GetValidity();
+
+   if ((state == gpt_invalid) && ((mbrState == mbr) || (mbrState == hybrid))) {
+      cout << "\n***************************************************************\n"
+           << "Found invalid GPT and valid MBR; converting MBR to GPT format\n"
+           << "in memory. ";
+      if (!justLooking) {
+         cout << "\aTHIS OPERATION IS POTENTIALLY DESTRUCTIVE! Exit by\n"
+              << "typing 'q' if you don't want to convert your MBR partitions\n"
+              << "to GPT format!";
+      } // if
+      cout << "\n***************************************************************\n\n";
+      which = use_mbr;
+   } // if
+
+   if ((state == gpt_invalid) && bsdFound) {
+      cout << "\n**********************************************************************\n"
+           << "Found invalid GPT and valid BSD disklabel; converting BSD disklabel\n"
+           << "to GPT format.";
+      if ((!justLooking) && (!beQuiet)) {
+      cout << "\a THIS OPERATION IS POTENTIALLY DESTRUCTIVE! Your first\n"
+           << "BSD partition will likely be unusable. Exit by typing 'q' if you don't\n"
+           << "want to convert your BSD partitions to GPT format!";
+      } // if
+      cout << "\n**********************************************************************\n\n";
+      which = use_bsd;
+   } // if
+
+   if ((state == gpt_valid) && (mbrState == gpt)) {
+      which = use_gpt;
+      if (!beQuiet)
+         cout << "Found valid GPT with protective MBR; using GPT.\n";
+   } // if
+   if ((state == gpt_valid) && (mbrState == hybrid)) {
+      which = use_gpt;
+      if (!beQuiet)
+         cout << "Found valid GPT with hybrid MBR; using GPT.\n";
+   } // if
+   if ((state == gpt_valid) && (mbrState == invalid)) {
+      cout << "\aFound valid GPT with corrupt MBR; using GPT and will write new\n"
+           << "protective MBR on save.\n";
+      which = use_gpt;
+   } // if
+   if ((state == gpt_valid) && (mbrState == mbr)) {
+      which = use_abort;
+   } // if
+
+   if (state == gpt_corrupt) {
+      if (mbrState == gpt) {
+         cout << "\a\a****************************************************************************\n"
+              << "Caution: Found protective or hybrid MBR and corrupt GPT. Using GPT, but disk\n"
+              << "verification and recovery are STRONGLY recommended.\n"
+              << "****************************************************************************\n";
+         which = use_gpt;
+      } else {
+         which = use_abort;
+      } // if/else MBR says disk is GPT
+   } // if GPT corrupt
+
+   if (which == use_new)
+      cout << "Creating new GPT entries.\n";
+
+   return which;
+} // UseWhichPartitions()
+
+// Convert MBR partition table into GPT form.
+void GPTData::XFormPartitions(void) {
+   int i, numToConvert;
+   uint8_t origType;
+
+   // Clear out old data & prepare basics....
+   ClearGPTData();
+
+   // Convert the smaller of the # of GPT or MBR partitions
+   if (numParts > MAX_MBR_PARTS)
+      numToConvert = MAX_MBR_PARTS;
+   else
+      numToConvert = numParts;
+
+   for (i = 0; i < numToConvert; i++) {
+      origType = protectiveMBR.GetType(i);
+      // don't waste CPU time trying to convert extended, hybrid protective, or
+      // null (non-existent) partitions
+      if ((origType != 0x05) && (origType != 0x0f) && (origType != 0x85) &&
+          (origType != 0x00) && (origType != 0xEE))
+         partitions[i] = protectiveMBR.AsGPT(i);
+   } // for
+
+   // Convert MBR into protective MBR
+   protectiveMBR.MakeProtectiveMBR();
+
+   // Record that all original CRCs were OK so as not to raise flags
+   // when doing a disk verification
+   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
+} // GPTData::XFormPartitions()
+
+// Transforms BSD disklabel on the specified partition (numbered from 0).
+// If an invalid partition number is given, the program does nothing.
+// Returns the number of new partitions created.
+int GPTData::XFormDisklabel(uint32_t partNum) {
+   uint32_t low, high;
+   int goOn = 1, numDone = 0;
+   BSDData disklabel;
+
+   if (GetPartRange(&low, &high) == 0) {
+      goOn = 0;
+      cout << "No partitions!\n";
+   } // if
+   if (partNum > high) {
+      goOn = 0;
+      cout << "Specified partition is invalid!\n";
+   } // if
+
+   // If all is OK, read the disklabel and convert it.
+   if (goOn) {
+      goOn = disklabel.ReadBSDData(&myDisk, partitions[partNum].GetFirstLBA(),
+                                   partitions[partNum].GetLastLBA());
+      if ((goOn) && (disklabel.IsDisklabel())) {
+         numDone = XFormDisklabel(&disklabel);
+         if (numDone == 1)
+            cout << "Converted 1 BSD partition.\n";
+         else
+            cout << "Converted " << numDone << " BSD partitions.\n";
+      } else {
+         cout << "Unable to convert partitions! Unrecognized BSD disklabel.\n";
+      } // if/else
+   } // if
+   if (numDone > 0) { // converted partitions; delete carrier
+      partitions[partNum].BlankPartition();
+   } // if
+   return numDone;
+} // GPTData::XFormDisklabel(uint32_t i)
+
+// Transform the partitions on an already-loaded BSD disklabel...
+int GPTData::XFormDisklabel(BSDData* disklabel) {
+   int i, partNum = 0, numDone = 0;
+
+   if (disklabel->IsDisklabel()) {
+      for (i = 0; i < disklabel->GetNumParts(); i++) {
+         partNum = FindFirstFreePart();
+         if (partNum >= 0) {
+            partitions[partNum] = disklabel->AsGPT(i);
+            if (partitions[partNum].IsUsed())
+               numDone++;
+         } // if
+      } // for
+      if (partNum == -1)
+         cerr << "Warning! Too many partitions to convert!\n";
+   } // if
+
+   // Record that all original CRCs were OK so as not to raise flags
+   // when doing a disk verification
+   mainCrcOk = secondCrcOk = mainPartsCrcOk = secondPartsCrcOk = 1;
+
+   return numDone;
+} // GPTData::XFormDisklabel(BSDData* disklabel)
+
+// Add one GPT partition to MBR. Used by PartsToMBR() functions. Created
+// partition has the active/bootable flag UNset and uses the GPT fdisk
+// type code divided by 0x0100 as the MBR type code.
+// Returns 1 if operation was 100% successful, 0 if there were ANY
+// problems.
+int GPTData::OnePartToMBR(uint32_t gptPart, int mbrPart) {
+   int allOK = 1;
+
+   if ((mbrPart < 0) || (mbrPart > 3)) {
+      cout << "MBR partition " << mbrPart + 1 << " is out of range; omitting it.\n";
+      allOK = 0;
+   } // if
+   if (gptPart >= numParts) {
+      cout << "GPT partition " << gptPart + 1 << " is out of range; omitting it.\n";
+      allOK = 0;
+   } // if
+   if (allOK && (partitions[gptPart].GetLastLBA() == UINT64_C(0))) {
+      cout << "GPT partition " << gptPart + 1 << " is undefined; omitting it.\n";
+      allOK = 0;
+   } // if
+   if (allOK && (partitions[gptPart].GetFirstLBA() <= UINT32_MAX) &&
+       (partitions[gptPart].GetLengthLBA() <= UINT32_MAX)) {
+      if (partitions[gptPart].GetLastLBA() > UINT32_MAX) {
+         cout << "Caution: Partition end point past 32-bit pointer boundary;"
+              << " some OSes may\nreact strangely.\n";
+      } // if
+      protectiveMBR.MakePart(mbrPart, (uint32_t) partitions[gptPart].GetFirstLBA(),
+                             (uint32_t) partitions[gptPart].GetLengthLBA(),
+                             partitions[gptPart].GetHexType() / 256, 0);
+   } else { // partition out of range
+      if (allOK) // Display only if "else" triggered by out-of-bounds condition
+         cout << "Partition " << gptPart + 1 << " begins beyond the 32-bit pointer limit of MBR "
+              << "partitions, or is\n too big; omitting it.\n";
+      allOK = 0;
+   } // if/else
+   return allOK;
+} // GPTData::OnePartToMBR()
+
+
+/**********************************************************************
+ *                                                                    *
+ * Functions that adjust GPT data structures WITHOUT user interaction *
+ * (they may display information for the user's benefit, though)      *
+ *                                                                    *
+ **********************************************************************/
+
+// Resizes GPT to specified number of entries. Creates a new table if
+// necessary, copies data if it already exists. If fillGPTSectors is 1
+// (the default), rounds numEntries to fill all the sectors necessary to
+// hold the GPT.
+// Returns 1 if all goes well, 0 if an error is encountered.
+int GPTData::SetGPTSize(uint32_t numEntries, int fillGPTSectors) {
+   GPTPart* newParts;
+   uint32_t i, high, copyNum, entriesPerSector;
+   int allOK = 1;
+
+   // First, adjust numEntries upward, if necessary, to get a number
+   // that fills the allocated sectors
+   entriesPerSector = blockSize / GPT_SIZE;
+   if (fillGPTSectors && ((numEntries % entriesPerSector) != 0)) {
+      cout << "Adjusting GPT size from " << numEntries << " to ";
+      numEntries = ((numEntries / entriesPerSector) + 1) * entriesPerSector;
+      cout << numEntries << " to fill the sector\n";
+   } // if
+
+   // Do the work only if the # of partitions is changing. Along with being
+   // 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 != numParts) || (partitions == NULL)) && (numEntries > 0)) {
+      newParts = new GPTPart [numEntries];
+      if (newParts != NULL) {
+         if (partitions != NULL) { // existing partitions; copy them over
+            GetPartRange(&i, &high);
+            if (numEntries < (high + 1)) { // Highest entry too high for new #
+               cout << "The highest-numbered partition is " << high + 1
+                    << ", which is greater than the requested\n"
+                    << "partition table size of " << numEntries
+                    << "; cannot resize. Perhaps sorting will help.\n";
+               allOK = 0;
+               delete[] newParts;
+            } else { // go ahead with copy
+               if (numEntries < numParts)
+                  copyNum = numEntries;
+               else
+                  copyNum = numParts;
+               for (i = 0; i < copyNum; i++) {
+                  newParts[i] = partitions[i];
+               } // for
+               delete[] partitions;
+               partitions = newParts;
+            } // if
+         } else { // No existing partition table; just create it
+            partitions = newParts;
+         } // if/else existing partitions
+         numParts = numEntries;
+         mainHeader.firstUsableLBA = ((numEntries * GPT_SIZE) / blockSize) + (((numEntries * GPT_SIZE) % blockSize) != 0) + 2 ;
+         secondHeader.firstUsableLBA = mainHeader.firstUsableLBA;
+         MoveSecondHeaderToEnd();
+         if (diskSize > 0)
+            CheckGPTSize();
+      } else { // Bad memory allocation
+         cerr << "Error allocating memory for partition table! Size is unchanged!\n";
+         allOK = 0;
+      } // if/else
+   } // if/else
+   mainHeader.numParts = numParts;
+   secondHeader.numParts = numParts;
+   return (allOK);
+} // GPTData::SetGPTSize()
+
+// Blank the partition array
+void GPTData::BlankPartitions(void) {
+   uint32_t i;
+
+   for (i = 0; i < numParts; i++) {
+      partitions[i].BlankPartition();
+   } // for
+} // GPTData::BlankPartitions()
+
+// Delete a partition by number. Returns 1 if successful,
+// 0 if there was a problem. Returns 1 if partition was in
+// range, 0 if it was out of range.
+int GPTData::DeletePartition(uint32_t partNum) {
+   uint64_t startSector, length;
+   uint32_t low, high, numUsedParts, retval = 1;;
+
+   numUsedParts = GetPartRange(&low, &high);
+   if ((numUsedParts > 0) && (partNum >= low) && (partNum <= high)) {
+      // In case there's a protective MBR, look for & delete matching
+      // MBR partition....
+      startSector = partitions[partNum].GetFirstLBA();
+      length = partitions[partNum].GetLengthLBA();
+      protectiveMBR.DeleteByLocation(startSector, length);
+
+      // Now delete the GPT partition
+      partitions[partNum].BlankPartition();
+   } else {
+      cerr << "Partition number " << partNum + 1 << " out of range!\n";
+      retval = 0;
+   } // if/else
+   return retval;
+} // GPTData::DeletePartition(uint32_t partNum)
+
+// Non-interactively create a partition.
+// Returns 1 if the operation was successful, 0 if a problem was discovered.
+uint32_t GPTData::CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector) {
+   int retval = 1; // assume there'll be no problems
+   uint64_t origSector = startSector;
+
+   if (IsFreePartNum(partNum)) {
+      if (Align(&startSector)) {
+         cout << "Information: Moved requested sector from " << origSector << " to "
+              << startSector << " in\norder to align on " << sectorAlignment
+              << "-sector boundaries.\n";
+      } // if
+      if (IsFree(startSector) && (startSector <= endSector)) {
+         if (FindLastInFree(startSector) >= endSector) {
+            partitions[partNum].SetFirstLBA(startSector);
+            partitions[partNum].SetLastLBA(endSector);
+            partitions[partNum].SetType(DEFAULT_GPT_TYPE);
+            partitions[partNum].RandomizeUniqueGUID();
+         } else retval = 0; // if free space until endSector
+      } else retval = 0; // if startSector is free
+   } else retval = 0; // if legal partition number
+   return retval;
+} // GPTData::CreatePartition(partNum, startSector, endSector)
+
+// Sort the GPT entries, eliminating gaps and making for a logical
+// ordering.
+void GPTData::SortGPT(void) {
+   if (numParts > 0)
+      sort(partitions, partitions + numParts);
+} // GPTData::SortGPT()
+
+// Swap the contents of two partitions.
+// Returns 1 if successful, 0 if either partition is out of range
+// (that is, not a legal number; either or both can be empty).
+// Note that if partNum1 = partNum2 and this number is in range,
+// it will be considered successful.
+int GPTData::SwapPartitions(uint32_t partNum1, uint32_t partNum2) {
+   GPTPart temp;
+   int allOK = 1;
+
+   if ((partNum1 < numParts) && (partNum2 < numParts)) {
+      if (partNum1 != partNum2) {
+         temp = partitions[partNum1];
+         partitions[partNum1] = partitions[partNum2];
+         partitions[partNum2] = temp;
+      } // if
+   } else allOK = 0; // partition numbers are valid
+   return allOK;
+} // GPTData::SwapPartitions()
+
+// Set up data structures for entirely new set of partitions on the
+// specified device. Returns 1 if OK, 0 if there were problems.
+// Note that this function does NOT clear the protectiveMBR data
+// structure, since it may hold the original MBR partitions if the
+// program was launched on an MBR disk, and those may need to be
+// converted to GPT format.
+int GPTData::ClearGPTData(void) {
+   int goOn = 1, i;
+
+   // Set up the partition table....
+   delete[] partitions;
+   partitions = NULL;
+   SetGPTSize(NUM_GPT_ENTRIES);
+
+   // Now initialize a bunch of stuff that's static....
+   mainHeader.signature = GPT_SIGNATURE;
+   mainHeader.revision = 0x00010000;
+   mainHeader.headerSize = HEADER_SIZE;
+   mainHeader.reserved = 0;
+   mainHeader.currentLBA = UINT64_C(1);
+   mainHeader.partitionEntriesLBA = (uint64_t) 2;
+   mainHeader.sizeOfPartitionEntries = GPT_SIZE;
+   for (i = 0; i < GPT_RESERVED; i++) {
+      mainHeader.reserved2[i] = '\0';
+   } // for
+   if (blockSize > 0)
+      sectorAlignment = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
+   else
+      sectorAlignment = DEFAULT_ALIGNMENT;
+
+   // Now some semi-static items (computed based on end of disk)
+   mainHeader.backupLBA = diskSize - UINT64_C(1);
+   mainHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+
+   // Set a unique GUID for the disk, based on random numbers
+   mainHeader.diskGUID.Randomize();
+
+   // Copy main header to backup header
+   RebuildSecondHeader();
+
+   // Blank out the partitions array....
+   BlankPartitions();
+
+   // Flag all CRCs as being OK....
+   mainCrcOk = 1;
+   secondCrcOk = 1;
+   mainPartsCrcOk = 1;
+   secondPartsCrcOk = 1;
+
+   return (goOn);
+} // GPTData::ClearGPTData()
+
+// Set the location of the second GPT header data to the end of the disk.
+// If the disk size has actually changed, this also adjusts the protective
+// entry in the MBR, since it's probably no longer correct.
+// Used internally and called by the 'e' option on the recovery &
+// transformation menu, to help users of RAID arrays who add disk space
+// to their arrays or to adjust data structures in restore operations
+// involving unequal-sized disks.
+void GPTData::MoveSecondHeaderToEnd() {
+   mainHeader.backupLBA = secondHeader.currentLBA = diskSize - UINT64_C(1);
+   if (mainHeader.lastUsableLBA != diskSize - mainHeader.firstUsableLBA) {
+      if (protectiveMBR.GetValidity() == hybrid) {
+         protectiveMBR.OptimizeEESize();
+         RecomputeCHS();
+      } // if
+      if (protectiveMBR.GetValidity() == gpt)
+         MakeProtectiveMBR();
+   } // if
+   mainHeader.lastUsableLBA = secondHeader.lastUsableLBA = diskSize - mainHeader.firstUsableLBA;
+   secondHeader.partitionEntriesLBA = secondHeader.lastUsableLBA + UINT64_C(1);
+} // GPTData::FixSecondHeaderLocation()
+
+// Sets the partition's name to the specified UnicodeString without
+// user interaction.
+// Returns 1 on success, 0 on failure (invalid partition number).
+int GPTData::SetName(uint32_t partNum, const UnicodeString & theName) {
+   int retval = 1;
+
+   if (IsUsedPartNum(partNum))
+      partitions[partNum].SetName(theName);
+   else
+      retval = 0;
+
+   return retval;
+} // GPTData::SetName
+
+// Set the disk GUID to the specified value. Note that the header CRCs must
+// be recomputed after calling this function.
+void GPTData::SetDiskGUID(GUIDData newGUID) {
+   mainHeader.diskGUID = newGUID;
+   secondHeader.diskGUID = newGUID;
+} // SetDiskGUID()
+
+// Set the unique GUID of the specified partition. Returns 1 on
+// successful completion, 0 if there were problems (invalid
+// partition number).
+int GPTData::SetPartitionGUID(uint32_t pn, GUIDData theGUID) {
+   int retval = 0;
+
+   if (pn < numParts) {
+      if (partitions[pn].IsUsed()) {
+         partitions[pn].SetUniqueGUID(theGUID);
+         retval = 1;
+      } // if
+   } // if
+   return retval;
+} // GPTData::SetPartitionGUID()
+
+// Set new random GUIDs for the disk and all partitions. Intended to be used
+// after disk cloning or similar operations that don't randomize the GUIDs.
+void GPTData::RandomizeGUIDs(void) {
+   uint32_t i;
+
+   mainHeader.diskGUID.Randomize();
+   secondHeader.diskGUID = mainHeader.diskGUID;
+   for (i = 0; i < numParts; i++)
+      if (partitions[i].IsUsed())
+         partitions[i].RandomizeUniqueGUID();
+} // GPTData::RandomizeGUIDs()
+
+// Change partition type code non-interactively. Returns 1 if
+// successful, 0 if not....
+int GPTData::ChangePartType(uint32_t partNum, PartType theGUID) {
+   int retval = 1;
+
+   if (!IsFreePartNum(partNum)) {
+      partitions[partNum].SetType(theGUID);
+   } else retval = 0;
+   return retval;
+} // GPTData::ChangePartType()
+
+// Recompute the CHS values of all the MBR partitions. Used to reset
+// CHS values that some BIOSes require, despite the fact that the
+// resulting CHS values violate the GPT standard.
+void GPTData::RecomputeCHS(void) {
+   int i;
+
+   for (i = 0; i < 4; i++)
+      protectiveMBR.RecomputeCHS(i);
+} // GPTData::RecomputeCHS()
+
+// Adjust sector number so that it falls on a sector boundary that's a
+// multiple of sectorAlignment. This is done to improve the performance
+// of Western Digital Advanced Format disks and disks with similar
+// technology from other companies, which use 4096-byte sectors
+// internally although they translate to 512-byte sectors for the
+// benefit of the OS. If partitions aren't properly aligned on these
+// disks, some filesystem data structures can span multiple physical
+// sectors, degrading performance. This function should be called
+// only on the FIRST sector of the partition, not the last!
+// This function returns 1 if the alignment was altered, 0 if it
+// was unchanged.
+int GPTData::Align(uint64_t* sector) {
+   int retval = 0, sectorOK = 0;
+   uint64_t earlier, later, testSector;
+
+   if ((*sector % sectorAlignment) != 0) {
+      earlier = (*sector / sectorAlignment) * sectorAlignment;
+      later = earlier + (uint64_t) sectorAlignment;
+
+      // Check to see that every sector between the earlier one and the
+      // requested one is clear, and that it's not too early....
+      if (earlier >= mainHeader.firstUsableLBA) {
+         sectorOK = 1;
+         testSector = earlier;
+         do {
+            sectorOK = IsFree(testSector++);
+         } while ((sectorOK == 1) && (testSector < *sector));
+         if (sectorOK == 1) {
+            *sector = earlier;
+            retval = 1;
+         } // if
+      } // if firstUsableLBA check
+
+      // If couldn't move the sector earlier, try to move it later instead....
+      if ((sectorOK != 1) && (later <= mainHeader.lastUsableLBA)) {
+         sectorOK = 1;
+         testSector = later;
+         do {
+            sectorOK = IsFree(testSector--);
+         } while ((sectorOK == 1) && (testSector > *sector));
+         if (sectorOK == 1) {
+            *sector = later;
+            retval = 1;
+         } // if
+      } // if
+   } // if
+   return retval;
+} // GPTData::Align()
+
+/********************************************************
+ *                                                      *
+ * Functions that return data about GPT data structures *
+ * (most of these are inline in gpt.h)                  *
+ *                                                      *
+ ********************************************************/
+
+// Find the low and high used partition numbers (numbered from 0).
+// Return value is the number of partitions found. Note that the
+// *low and *high values are both set to 0 when no partitions
+// are found, as well as when a single partition in the first
+// position exists. Thus, the return value is the only way to
+// tell when no partitions exist.
+int GPTData::GetPartRange(uint32_t *low, uint32_t *high) {
+   uint32_t i;
+   int numFound = 0;
+
+   *low = numParts + 1; // code for "not found"
+   *high = 0;
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) { // it exists
+         *high = i; // since we're counting up, set the high value
+         // Set the low value only if it's not yet found...
+         if (*low == (numParts + 1)) *low = i;
+            numFound++;
+      } // if
+   } // for
+
+   // Above will leave *low pointing to its "not found" value if no partitions
+   // are defined, so reset to 0 if this is the case....
+   if (*low == (numParts + 1))
+      *low = 0;
+   return numFound;
+} // GPTData::GetPartRange()
+
+// Returns the value of the first free partition, or -1 if none is
+// unused.
+int GPTData::FindFirstFreePart(void) {
+   int i = 0;
+
+   if (partitions != NULL) {
+      while ((i < (int) numParts) && (partitions[i].IsUsed()))
+         i++;
+      if (i >= (int) numParts)
+         i = -1;
+   } else i = -1;
+   return i;
+} // GPTData::FindFirstFreePart()
+
+// Returns the number of defined partitions.
+uint32_t GPTData::CountParts(void) {
+   uint32_t i, counted = 0;
+
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed())
+         counted++;
+   } // for
+   return counted;
+} // GPTData::CountParts()
+
+/****************************************************
+ *                                                  *
+ * Functions that return data about disk free space *
+ *                                                  *
+ ****************************************************/
+
+// Find the first available block after the starting point; returns 0 if
+// there are no available blocks left
+uint64_t GPTData::FindFirstAvailable(uint64_t start) {
+   uint64_t first;
+   uint32_t i;
+   int firstMoved = 0;
+
+   // Begin from the specified starting point or from the first usable
+   // LBA, whichever is greater...
+   if (start < mainHeader.firstUsableLBA)
+      first = mainHeader.firstUsableLBA;
+   else
+      first = start;
+
+   // ...now search through all partitions; if first is within an
+   // existing partition, move it to the next sector after that
+   // partition and repeat. If first was moved, set firstMoved
+   // flag; repeat until firstMoved is not set, so as to catch
+   // cases where partitions are out of sequential order....
+   do {
+      firstMoved = 0;
+      for (i = 0; i < numParts; i++) {
+         if ((partitions[i].IsUsed()) && (first >= partitions[i].GetFirstLBA()) &&
+             (first <= partitions[i].GetLastLBA())) { // in existing part.
+            first = partitions[i].GetLastLBA() + 1;
+            firstMoved = 1;
+         } // if
+      } // for
+   } while (firstMoved == 1);
+   if (first > mainHeader.lastUsableLBA)
+      first = 0;
+   return (first);
+} // GPTData::FindFirstAvailable()
+
+// Finds the first available sector in the largest block of unallocated
+// space on the disk. Returns 0 if there are no available blocks left
+uint64_t GPTData::FindFirstInLargest(void) {
+   uint64_t start, firstBlock, lastBlock, segmentSize, selectedSize = 0, selectedSegment = 0;
+
+   start = 0;
+   do {
+      firstBlock = FindFirstAvailable(start);
+      if (firstBlock != UINT32_C(0)) { // something's free...
+         lastBlock = FindLastInFree(firstBlock);
+         segmentSize = lastBlock - firstBlock + UINT32_C(1);
+         if (segmentSize > selectedSize) {
+            selectedSize = segmentSize;
+            selectedSegment = firstBlock;
+         } // if
+         start = lastBlock + 1;
+      } // if
+   } while (firstBlock != 0);
+   return selectedSegment;
+} // GPTData::FindFirstInLargest()
+
+// Find the last available block on the disk.
+// Returns 0 if there are no available sectors
+uint64_t GPTData::FindLastAvailable(void) {
+   uint64_t last;
+   uint32_t i;
+   int lastMoved = 0;
+
+   // Start by assuming the last usable LBA is available....
+   last = mainHeader.lastUsableLBA;
+
+   // ...now, similar to algorithm in FindFirstAvailable(), search
+   // through all partitions, moving last when it's in an existing
+   // partition. Set the lastMoved flag so we repeat to catch cases
+   // where partitions are out of logical order.
+   do {
+      lastMoved = 0;
+      for (i = 0; i < numParts; i++) {
+         if ((last >= partitions[i].GetFirstLBA()) &&
+             (last <= partitions[i].GetLastLBA())) { // in existing part.
+            last = partitions[i].GetFirstLBA() - 1;
+            lastMoved = 1;
+         } // if
+      } // for
+   } while (lastMoved == 1);
+   if (last < mainHeader.firstUsableLBA)
+      last = 0;
+   return (last);
+} // GPTData::FindLastAvailable()
+
+// Find the last available block in the free space pointed to by start.
+uint64_t GPTData::FindLastInFree(uint64_t start) {
+   uint64_t nearestStart;
+   uint32_t i;
+
+   nearestStart = mainHeader.lastUsableLBA;
+   for (i = 0; i < numParts; i++) {
+      if ((nearestStart > partitions[i].GetFirstLBA()) &&
+          (partitions[i].GetFirstLBA() > start)) {
+         nearestStart = partitions[i].GetFirstLBA() - 1;
+      } // if
+   } // for
+   return (nearestStart);
+} // GPTData::FindLastInFree()
+
+// Finds the total number of free blocks, the number of segments in which
+// they reside, and the size of the largest of those segments
+uint64_t GPTData::FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment) {
+   uint64_t start = UINT64_C(0); // starting point for each search
+   uint64_t totalFound = UINT64_C(0); // running total
+   uint64_t firstBlock; // first block in a segment
+   uint64_t lastBlock; // last block in a segment
+   uint64_t segmentSize; // size of segment in blocks
+   uint32_t num = 0;
+
+   *largestSegment = UINT64_C(0);
+   if (diskSize > 0) {
+      do {
+         firstBlock = FindFirstAvailable(start);
+         if (firstBlock != UINT64_C(0)) { // something's free...
+            lastBlock = FindLastInFree(firstBlock);
+            segmentSize = lastBlock - firstBlock + UINT64_C(1);
+            if (segmentSize > *largestSegment) {
+               *largestSegment = segmentSize;
+            } // if
+            totalFound += segmentSize;
+            num++;
+            start = lastBlock + 1;
+         } // if
+      } while (firstBlock != 0);
+   } // if
+   *numSegments = num;
+   return totalFound;
+} // GPTData::FindFreeBlocks()
+
+// 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;
+
+   for (i = 0; i < numParts; i++) {
+      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()
+
+// Returns 1 if partNum is unused AND if it's a legal value.
+int GPTData::IsFreePartNum(uint32_t partNum) {
+   return ((partNum < numParts) && (partitions != NULL) &&
+           (!partitions[partNum].IsUsed()));
+} // GPTData::IsFreePartNum()
+
+// Returns 1 if partNum is in use.
+int GPTData::IsUsedPartNum(uint32_t partNum) {
+   return ((partNum < numParts) && (partitions != NULL) &&
+           (partitions[partNum].IsUsed()));
+} // GPTData::IsUsedPartNum()
+
+/***********************************************************
+ *                                                         *
+ * Change how functions work or return information on them *
+ *                                                         *
+ ***********************************************************/
+
+// Set partition alignment value; partitions will begin on multiples of
+// the specified value
+void GPTData::SetAlignment(uint32_t n) {
+   if (n > 0)
+      sectorAlignment = n;
+   else
+      cerr << "Attempt to set partition alignment to 0!\n";
+} // GPTData::SetAlignment()
+
+// Compute sector alignment based on the current partitions (if any). Each
+// partition's starting LBA is examined, and if it's divisible by a power-of-2
+// value less than or equal to the DEFAULT_ALIGNMENT value (adjusted for the
+// sector size), but not by the previously-located alignment value, then the
+// alignment value is adjusted down. If the computed alignment is less than 8
+// and the disk is bigger than SMALLEST_ADVANCED_FORMAT, resets it to 8. This
+// is a safety measure for Advanced Format drives. If no partitions are
+// defined, the alignment value is set to DEFAULT_ALIGNMENT (2048) (or an
+// adjustment of that based on the current sector size). The result is that new
+// drives are aligned to 2048-sector multiples but the program won't complain
+// about other alignments on existing disks unless a smaller-than-8 alignment
+// is used on big disks (as safety for Advanced Format drives).
+// Returns the computed alignment value.
+uint32_t GPTData::ComputeAlignment(void) {
+   uint32_t i = 0, found, exponent = 31;
+   uint32_t align = DEFAULT_ALIGNMENT;
+
+   if (blockSize > 0)
+      align = DEFAULT_ALIGNMENT * SECTOR_SIZE / blockSize;
+   exponent = (uint32_t) log2(align);
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) {
+         found = 0;
+         while (!found) {
+            align = UINT64_C(1) << exponent;
+            if ((partitions[i].GetFirstLBA() % align) == 0) {
+               found = 1;
+            } else {
+               exponent--;
+            } // if/else
+         } // while
+      } // if
+   } // for
+   if ((align < MIN_AF_ALIGNMENT) && (diskSize >= SMALLEST_ADVANCED_FORMAT))
+      align = MIN_AF_ALIGNMENT;
+   sectorAlignment = align;
+   return align;
+} // GPTData::ComputeAlignment()
+
+/********************************
+ *                              *
+ * Endianness support functions *
+ *                              *
+ ********************************/
+
+void GPTData::ReverseHeaderBytes(struct GPTHeader* header) {
+   ReverseBytes(&header->signature, 8);
+   ReverseBytes(&header->revision, 4);
+   ReverseBytes(&header->headerSize, 4);
+   ReverseBytes(&header->headerCRC, 4);
+   ReverseBytes(&header->reserved, 4);
+   ReverseBytes(&header->currentLBA, 8);
+   ReverseBytes(&header->backupLBA, 8);
+   ReverseBytes(&header->firstUsableLBA, 8);
+   ReverseBytes(&header->lastUsableLBA, 8);
+   ReverseBytes(&header->partitionEntriesLBA, 8);
+   ReverseBytes(&header->numParts, 4);
+   ReverseBytes(&header->sizeOfPartitionEntries, 4);
+   ReverseBytes(&header->partitionEntriesCRC, 4);
+   ReverseBytes(header->reserved2, GPT_RESERVED);
+} // GPTData::ReverseHeaderBytes()
+
+// Reverse byte order for all partitions.
+void GPTData::ReversePartitionBytes() {
+   uint32_t i;
+
+   for (i = 0; i < numParts; i++) {
+      partitions[i].ReversePartBytes();
+   } // for
+} // GPTData::ReversePartitionBytes()
+
+// Validate partition number
+bool GPTData::ValidPartNum (const uint32_t partNum) {
+   if (partNum >= numParts) {
+      cerr << "Partition number out of range: " << partNum << "\n";
+      return false;
+   } // if
+   return true;
+} // GPTData::ValidPartNum
+
+// Return a single partition for inspection (not modification!) by other
+// functions.
+const GPTPart & GPTData::operator[](uint32_t partNum) const {
+   if (partNum >= numParts) {
+      cerr << "Partition number out of range (" << partNum << " requested, but only "
+           << numParts << " available)\n";
+      exit(1);
+   } // if
+   if (partitions == NULL) {
+      cerr << "No partitions defined in GPTData::operator[]; fatal error!\n";
+      exit(1);
+   } // if
+   return partitions[partNum];
+} // operator[]
+
+// Return (not for modification!) the disk's GUID value
+const GUIDData & GPTData::GetDiskGUID(void) const {
+   return mainHeader.diskGUID;
+} // GPTData::GetDiskGUID()
+
+// Manage attributes for a partition, based on commands passed to this function.
+// (Function is non-interactive.)
+// Returns 1 if a modification command succeeded, 0 if the command should not have
+// modified data, and -1 if a modification command failed.
+int GPTData::ManageAttributes(int partNum, const string & command, const string & bits) {
+   int retval = 0;
+   Attributes theAttr;
+
+   if (partNum >= (int) numParts) {
+      cerr << "Invalid partition number (" << partNum + 1 << ")\n";
+      retval = -1;
+   } else {
+      if (command == "show") {
+         ShowAttributes(partNum);
+      } else if (command == "get") {
+         GetAttribute(partNum, bits);
+      } else {
+         theAttr = partitions[partNum].GetAttributes();
+         if (theAttr.OperateOnAttributes(partNum, command, bits)) {
+            partitions[partNum].SetAttributes(theAttr.GetAttributes());
+            retval = 1;
+         } else {
+            retval = -1;
+         } // if/else
+      } // if/elseif/else
+   } // if/else invalid partition #
+
+   return retval;
+} // GPTData::ManageAttributes()
+
+// Show all attributes for a specified partition....
+void GPTData::ShowAttributes(const uint32_t partNum) {
+   if ((partNum < numParts) && partitions[partNum].IsUsed())
+      partitions[partNum].ShowAttributes(partNum);
+} // GPTData::ShowAttributes
+
+// Show whether a single attribute bit is set (terse output)...
+void GPTData::GetAttribute(const uint32_t partNum, const string& attributeBits) {
+   if (partNum < numParts)
+      partitions[partNum].GetAttributes().OperateOnAttributes(partNum, "get", attributeBits);
+} // GPTData::GetAttribute
+
+
+/******************************************
+ *                                        *
+ * Additional non-class support functions *
+ *                                        *
+ ******************************************/
+
+// Check to be sure that data type sizes are correct. The basic types (uint*_t) should
+// never fail these tests, but the struct types may fail depending on compile options.
+// Specifically, the -fpack-struct option to gcc may be required to ensure proper structure
+// sizes.
+int SizesOK(void) {
+   int allOK = 1;
+
+   if (sizeof(uint8_t) != 1) {
+      cerr << "uint8_t is " << sizeof(uint8_t) << " bytes, should be 1 byte; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(uint16_t) != 2) {
+      cerr << "uint16_t is " << sizeof(uint16_t) << " bytes, should be 2 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(uint32_t) != 4) {
+      cerr << "uint32_t is " << sizeof(uint32_t) << " bytes, should be 4 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(uint64_t) != 8) {
+      cerr << "uint64_t is " << sizeof(uint64_t) << " bytes, should be 8 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(struct MBRRecord) != 16) {
+      cerr << "MBRRecord is " << sizeof(MBRRecord) << " bytes, should be 16 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(struct TempMBR) != 512) {
+      cerr << "TempMBR is " <<  sizeof(TempMBR) << " bytes, should be 512 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(struct GPTHeader) != 512) {
+      cerr << "GPTHeader is " << sizeof(GPTHeader) << " bytes, should be 512 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(GPTPart) != 128) {
+      cerr << "GPTPart is " << sizeof(GPTPart) << " bytes, should be 128 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(GUIDData) != 16) {
+      cerr << "GUIDData is " << sizeof(GUIDData) << " bytes, should be 16 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   if (sizeof(PartType) != 16) {
+      cerr << "PartType is " << sizeof(PartType) << " bytes, should be 16 bytes; aborting!\n";
+      allOK = 0;
+   } // if
+   return (allOK);
+} // SizesOK()
+
diff --git a/gptfdisk/gpt.h b/gptfdisk/gpt.h
new file mode 100644
index 0000000..e9afd06
--- /dev/null
+++ b/gptfdisk/gpt.h
@@ -0,0 +1,209 @@
+/* gpt.h -- GPT and data structure definitions, types, and
+   functions */
+
+/* This program is copyright (c) 2009-2011 by Roderick W. Smith. It is distributed
+  under the terms of the GNU GPL version 2, as detailed in the COPYING file. */
+
+#include <stdint.h>
+#include <sys/types.h>
+#include "gptpart.h"
+#include "support.h"
+#include "mbr.h"
+#include "bsd.h"
+#include "gptpart.h"
+
+#ifndef __GPTSTRUCTS
+#define __GPTSTRUCTS
+
+// Default values for sector alignment
+#define DEFAULT_ALIGNMENT 2048
+#define MAX_ALIGNMENT 65536
+#define MIN_AF_ALIGNMENT 8
+
+// Below constant corresponds to a ~279GiB (300GB) disk, since the
+// smallest Advanced Format drive I know of is 320GB in size
+#define SMALLEST_ADVANCED_FORMAT UINT64_C(585937500)
+
+using namespace std;
+
+/****************************************
+ *                                      *
+ * GPTData class and related structures *
+ *                                      *
+ ****************************************/
+
+// Validity state of GPT data
+enum GPTValidity {gpt_valid, gpt_corrupt, gpt_invalid};
+
+// Which set of partition data to use
+enum WhichToUse {use_gpt, use_mbr, use_bsd, use_new, use_abort};
+
+// Header (first 512 bytes) of GPT table
+#pragma pack(1)
+struct GPTHeader {
+   uint64_t signature;
+   uint32_t revision;
+   uint32_t headerSize;
+   uint32_t headerCRC;
+   uint32_t reserved;
+   uint64_t currentLBA;
+   uint64_t backupLBA;
+   uint64_t firstUsableLBA;
+   uint64_t lastUsableLBA;
+   GUIDData diskGUID;
+   uint64_t partitionEntriesLBA;
+   uint32_t numParts;
+   uint32_t sizeOfPartitionEntries;
+   uint32_t partitionEntriesCRC;
+   unsigned char reserved2[GPT_RESERVED];
+}; // struct GPTHeader
+
+// Data in GPT format
+class GPTData {
+protected:
+   struct GPTHeader mainHeader;
+   GPTPart *partitions;
+   uint32_t numParts; // # of partitions the table can hold
+   struct GPTHeader secondHeader;
+   MBRData protectiveMBR;
+   string device; // device filename
+   DiskIO myDisk;
+   uint32_t blockSize; // device block size
+   uint64_t diskSize; // size of device, in blocks
+   GPTValidity state; // is GPT valid?
+   int justLooking; // Set to 1 if program launched with "-l" or if read-only
+   int mainCrcOk;
+   int secondCrcOk;
+   int mainPartsCrcOk;
+   int secondPartsCrcOk;
+   int apmFound; // set to 1 if APM detected
+   int bsdFound; // set to 1 if BSD disklabel detected in MBR
+   uint32_t sectorAlignment; // Start partitions at multiples of sectorAlignment
+   int beQuiet;
+   WhichToUse whichWasUsed;
+
+   int LoadHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector, int *crcOk);
+   int LoadPartitionTable(const struct GPTHeader & header, DiskIO & disk, uint64_t sector = 0);
+   int CheckTable(struct GPTHeader *header);
+   int SaveHeader(struct GPTHeader *header, DiskIO & disk, uint64_t sector);
+   int SavePartitionTable(DiskIO & disk, uint64_t sector);
+public:
+   // Basic necessary functions....
+   GPTData(void);
+   GPTData(string deviceFilename);
+   virtual ~GPTData(void);
+   GPTData & operator=(const GPTData & orig);
+
+   // Verify (or update) data integrity
+   int Verify(void);
+   int CheckGPTSize(void);
+   int CheckHeaderValidity(void);
+   int CheckHeaderCRC(struct GPTHeader* header, int warn = 0);
+   void RecomputeCRCs(void);
+   void RebuildMainHeader(void);
+   void RebuildSecondHeader(void);
+   int VerifyMBR(void) {return protectiveMBR.FindOverlaps();}
+   int FindHybridMismatches(void);
+   int FindOverlaps(void);
+   int FindInsanePartitions(void);
+
+   // Load or save data from/to disk
+   int SetDisk(const string & deviceFilename);
+   DiskIO* GetDisk(void) {return &myDisk;}
+   int LoadMBR(const string & f) {return protectiveMBR.ReadMBRData(f);}
+   int WriteProtectiveMBR(void) {return protectiveMBR.WriteMBRData(&myDisk);}
+   void PartitionScan(void);
+   int LoadPartitions(const string & deviceFilename);
+   int ForceLoadGPTData(void);
+   int LoadMainTable(void);
+   int LoadSecondTableAsMain(void);
+   int SaveGPTData(int quiet = 0);
+   int SaveGPTBackup(const string & filename);
+   int LoadGPTBackup(const string & filename);
+   int SaveMBR(void);
+   int DestroyGPT(void);
+   int DestroyMBR(void);
+
+   // Display data....
+   void ShowAPMState(void);
+   void ShowGPTState(void);
+   void DisplayGPTData(void);
+   void DisplayMBRData(void) {protectiveMBR.DisplayMBRData();}
+   void ShowPartDetails(uint32_t partNum);
+
+   // Convert between GPT and other formats
+   virtual WhichToUse UseWhichPartitions(void);
+   void XFormPartitions(void);
+   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
+
+   // Adjust GPT structures WITHOUT user interaction...
+   int SetGPTSize(uint32_t numEntries, int fillGPTSectors = 1);
+   void BlankPartitions(void);
+   int DeletePartition(uint32_t partNum);
+   uint32_t CreatePartition(uint32_t partNum, uint64_t startSector, uint64_t endSector);
+   void SortGPT(void);
+   int SwapPartitions(uint32_t partNum1, uint32_t partNum2);
+   int ClearGPTData(void);
+   void MoveSecondHeaderToEnd();
+   int SetName(uint32_t partNum, const UnicodeString & theName);
+   void SetDiskGUID(GUIDData newGUID);
+   int SetPartitionGUID(uint32_t pn, GUIDData theGUID);
+   void RandomizeGUIDs(void);
+   int ChangePartType(uint32_t pn, PartType theGUID);
+   void MakeProtectiveMBR(void) {protectiveMBR.MakeProtectiveMBR();}
+   void RecomputeCHS(void);
+   int Align(uint64_t* sector);
+   void SetProtectiveMBR(BasicMBRData & newMBR) {protectiveMBR = newMBR;}
+   
+   // Return data about the GPT structures....
+   WhichToUse GetState(void) {return whichWasUsed;}
+   int GetPartRange(uint32_t* low, uint32_t* high);
+   int FindFirstFreePart(void);
+   uint32_t GetNumParts(void) {return mainHeader.numParts;}
+   uint64_t GetMainHeaderLBA(void) {return mainHeader.currentLBA;}
+   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;}
+   uint32_t CountParts(void);
+   bool ValidPartNum (const uint32_t partNum);
+   const GPTPart & operator[](uint32_t partNum) const;
+   const GUIDData & GetDiskGUID(void) const;
+   uint32_t GetBlockSize(void) {return blockSize;}
+
+   // Find information about free space
+   uint64_t FindFirstAvailable(uint64_t start = 0);
+   uint64_t FindFirstInLargest(void);
+   uint64_t FindLastAvailable();
+   uint64_t FindLastInFree(uint64_t start);
+   uint64_t FindFreeBlocks(uint32_t *numSegments, uint64_t *largestSegment);
+   int IsFree(uint64_t sector, uint32_t *partNum = NULL);
+   int IsFreePartNum(uint32_t partNum);
+   int IsUsedPartNum(uint32_t partNum);
+
+   // Change how functions work, or return information on same
+   void SetAlignment(uint32_t n);
+   uint32_t ComputeAlignment(void); // Set alignment based on current partitions
+   uint32_t GetAlignment(void) {return sectorAlignment;}
+   void JustLooking(int i = 1) {justLooking = i;}
+   void BeQuiet(int i = 1) {beQuiet = i;}
+   WhichToUse WhichWasUsed(void) {return whichWasUsed;}
+
+   // Endianness functions
+   void ReverseHeaderBytes(struct GPTHeader* header);
+   void ReversePartitionBytes(); // for endianness
+
+   // Attributes functions
+   int ManageAttributes(int partNum, const string & command, const string & bits);
+   void ShowAttributes(const uint32_t partNum);
+   void GetAttribute(const uint32_t partNum, const string& attributeBits);
+
+}; // class GPTData
+
+// Function prototypes....
+int SizesOK(void);
+
+#endif
diff --git a/gptfdisk/gptcl.cc b/gptfdisk/gptcl.cc
new file mode 100644
index 0000000..bdb4693
--- /dev/null
+++ b/gptfdisk/gptcl.cc
@@ -0,0 +1,568 @@
+/*
+    Implementation of GPTData class derivative with popt-based command
+    line processing
+    Copyright (C) 2010-2014 Roderick W. Smith
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+*/
+
+#include <string.h>
+#include <string>
+#include <iostream>
+#include <sstream>
+#include <errno.h>
+#include <popt.h>
+#include "gptcl.h"
+
+GPTDataCL::GPTDataCL(void) {
+   attributeOperation = backupFile = partName = hybrids = newPartInfo = NULL;
+   mbrParts = twoParts = outDevice = typeCode = partGUID = diskGUID = NULL;
+   alignment = DEFAULT_ALIGNMENT;
+   deletePartNum = infoPartNum = largestPartNum = bsdPartNum = 0;
+   tableSize = GPT_SIZE;
+} // GPTDataCL constructor
+
+GPTDataCL::GPTDataCL(string filename) {
+} // GPTDataCL constructor with filename
+
+GPTDataCL::~GPTDataCL(void) {
+} // GPTDataCL destructor
+
+void GPTDataCL::LoadBackupFile(string backupFile, int &saveData, int &neverSaveData) {
+   if (LoadGPTBackup(backupFile) == 1) {
+      JustLooking(0);
+      saveData = 1;
+   } else {
+      saveData = 0;
+      neverSaveData = 1;
+      cerr << "Error loading backup file!\n";
+   } // else
+} // GPTDataCL::LoadBackupFile()
+
+// Perform the actions specified on the command line. This is necessarily one
+// monster of a function!
+// Returns values:
+// 0 = success
+// 1 = too few arguments
+// 2 = error when reading partition table
+// 3 = non-GPT disk and no -g option
+// 4 = unable to save changes
+// 8 = disk replication operation (-R) failed
+int GPTDataCL::DoOptions(int argc, char* argv[]) {
+   GPTData secondDevice;
+   int opt, numOptions = 0, saveData = 0, neverSaveData = 0;
+   int partNum = 0, newPartNum = -1, saveNonGPT = 1, retval = 0, pretend = 0;
+   uint64_t low, high, startSector, endSector, sSize;
+   uint64_t temp; // temporary variable; free to use in any case
+   char *device;
+   string cmd, typeGUID, name;
+   PartType typeHelper;
+
+   struct poptOption theOptions[] =
+   {
+      {"attributes", 'A', POPT_ARG_STRING, &attributeOperation, 'A', "operate on partition attributes",
+          "list|[partnum:show|or|nand|xor|=|set|clear|toggle|get[:bitnum|hexbitmask]]"},
+      {"set-alignment", 'a', POPT_ARG_INT, &alignment, 'a', "set sector alignment", "value"},
+      {"backup", 'b', POPT_ARG_STRING, &backupFile, 'b', "backup GPT to file", "file"},
+      {"change-name", 'c', POPT_ARG_STRING, &partName, 'c', "change partition's name", "partnum:name"},
+      {"recompute-chs", 'C', POPT_ARG_NONE, NULL, 'C', "recompute CHS values in protective/hybrid MBR", ""},
+      {"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", ""},
+      {"first-aligned-in-largest", 'F', POPT_ARG_NONE, NULL, 'F', "show start of the largest free block, aligned", ""},
+      {"mbrtogpt", 'g', POPT_ARG_NONE, NULL, 'g', "convert MBR to GPT", ""},
+      {"randomize-guids", 'G', POPT_ARG_NONE, NULL, 'G', "randomize disk and partition GUIDs", ""},
+      {"hybrid", 'h', POPT_ARG_STRING, &hybrids, 'h', "create hybrid MBR", "partnum[:partnum...]"},
+      {"info", 'i', POPT_ARG_INT, &infoPartNum, 'i', "show detailed information on partition", "partnum"},
+      {"load-backup", 'l', POPT_ARG_STRING, &backupFile, 'l', "load GPT backup from file", "file"},
+      {"list-types", 'L', POPT_ARG_NONE, NULL, 'L', "list known partition types", ""},
+      {"gpttombr", 'm', POPT_ARG_STRING, &mbrParts, 'm', "convert GPT to MBR", "partnum[:partnum...]"},
+      {"new", 'n', POPT_ARG_STRING, &newPartInfo, 'n', "create new partition", "partnum:start:end"},
+      {"largest-new", 'N', POPT_ARG_INT, &largestPartNum, 'N', "create largest possible new partition", "partnum"},
+      {"clear", 'o', POPT_ARG_NONE, NULL, 'o', "clear partition table", ""},
+      {"print-mbr", 'O', POPT_ARG_NONE, NULL, 'O', "print MBR partition table", ""},
+      {"print", 'p', POPT_ARG_NONE, NULL, 'p', "print partition table", ""},
+      {"pretend", 'P', POPT_ARG_NONE, NULL, 'P', "make changes in memory, but don't write them", ""},
+      {"transpose", 'r', POPT_ARG_STRING, &twoParts, 'r', "transpose two partitions", "partnum:partnum"},
+      {"replicate", 'R', POPT_ARG_STRING, &outDevice, 'R', "replicate partition table", "device_filename"},
+      {"sort", 's', POPT_ARG_NONE, NULL, 's', "sort partition table entries", ""},
+      {"resize-table", 'S', POPT_ARG_INT, &tableSize, 'S', "resize partition table", "numparts"},
+      {"typecode", 't', POPT_ARG_STRING, &typeCode, 't', "change partition type code", "partnum:{hexcode|GUID}"},
+      {"transform-bsd", 'T', POPT_ARG_INT, &bsdPartNum, 'T', "transform BSD disklabel partition to GPT", "partnum"},
+      {"partition-guid", 'u', POPT_ARG_STRING, &partGUID, 'u', "set partition GUID", "partnum:guid"},
+      {"disk-guid", 'U', POPT_ARG_STRING, &diskGUID, 'U', "set disk GUID", "guid"},
+      {"verify", 'v', POPT_ARG_NONE, NULL, 'v', "check partition table integrity", ""},
+      {"version", 'V', POPT_ARG_NONE, NULL, 'V', "display version information", ""},
+      {"zap", 'z', POPT_ARG_NONE, NULL, 'z', "zap (destroy) GPT (but not MBR) data structures", ""},
+      {"zap-all", 'Z', POPT_ARG_NONE, NULL, 'Z', "zap (destroy) GPT and MBR data structures", ""},
+      POPT_AUTOHELP { NULL, 0, 0, NULL, 0, NULL, NULL }
+   };
+
+   // Create popt context...
+   poptCon = poptGetContext(NULL, argc, (const char**) argv, theOptions, 0);
+
+   poptSetOtherOptionHelp(poptCon, " [OPTION...] <device>");
+
+   if (argc < 2) {
+      poptPrintUsage(poptCon, stderr, 0);
+      return 1;
+   }
+
+   // Do one loop through the options to find the device filename and deal
+   // with options that don't require a device filename, to flag destructive
+   // (o, z, or Z) options, and to flag presence of a --pretend/-P option
+   while ((opt = poptGetNextOpt(poptCon)) > 0) {
+      switch (opt) {
+         case 'A':
+            cmd = GetString(attributeOperation, 1);
+            if (cmd == "list")
+               Attributes::ListAttributes();
+            break;
+         case 'L':
+            typeHelper.ShowAllTypes(0);
+            break;
+         case 'P':
+            pretend = 1;
+            break;
+         case 'V':
+            cout << "GPT fdisk (sgdisk) version " << GPTFDISK_VERSION << "\n\n";
+            break;
+         default:
+            break;
+      } // switch
+      numOptions++;
+   } // while
+
+   // Assume first non-option argument is the device filename....
+   device = (char*) poptGetArg(poptCon);
+   poptResetContext(poptCon);
+
+   if (device != NULL) {
+      JustLooking(); // reset as necessary
+      BeQuiet(); // Tell called functions to be less verbose & interactive
+      if (LoadPartitions((string) device)) {
+         if ((WhichWasUsed() == use_mbr) || (WhichWasUsed() == use_bsd))
+            saveNonGPT = 0; // flag so we don't overwrite unless directed to do so
+            sSize = GetBlockSize();
+         while ((opt = poptGetNextOpt(poptCon)) > 0) {
+            switch (opt) {
+               case 'A': {
+                  if (cmd != "list") {
+                     partNum = (int) GetInt(attributeOperation, 1) - 1;
+                     if (partNum < 0)
+                        partNum = newPartNum;
+                     if ((partNum >= 0) && (partNum < (int) GetNumParts())) {
+                        switch (ManageAttributes(partNum, GetString(attributeOperation, 2),
+                           GetString(attributeOperation, 3))) {
+                           case -1:
+                              saveData = 0;
+                              neverSaveData = 1;
+                              break;
+                           case 1:
+                              JustLooking(0);
+                              saveData = 1;
+                              break;
+                           default:
+                              break;
+                        } // switch
+                     } else {
+                        cerr << "Error: Invalid partition number " << partNum + 1 << "\n";
+                        saveData = 0;
+                        neverSaveData = 1;
+                     } // if/else reasonable partition #
+                  } // if (cmd != "list")
+                  break;
+               } // case 'A':
+               case 'a':
+                  SetAlignment(alignment);
+                  break;
+               case 'b':
+                  SaveGPTBackup(backupFile);
+                  free(backupFile);
+                  break;
+               case 'c':
+                  cout << "Setting name!\n";
+                  JustLooking(0);
+                  partNum = (int) GetInt(partName, 1) - 1;
+                  if (partNum < 0)
+                     partNum = newPartNum;
+                  cout << "partNum is " << partNum << "\n";
+                  if ((partNum >= 0) && (partNum < (int) GetNumParts())) {
+                     cout << "REALLY setting name!\n";
+                     name = GetString(partName, 2);
+                     if (SetName(partNum, (UnicodeString) name.c_str())) {
+                        saveData = 1;
+                     } else {
+                        cerr << "Unable to set partition " << partNum + 1
+                             << "'s name to '" << GetString(partName, 2) << "'!\n";
+                        neverSaveData = 1;
+                     } // if/else
+                     free(partName);
+                  }
+                  break;
+               case 'C':
+                  JustLooking(0);
+                  RecomputeCHS();
+                  saveData = 1;
+                  break;
+               case 'd':
+                  JustLooking(0);
+                  if (DeletePartition(deletePartNum - 1) == 0) {
+                     cerr << "Error " << errno << " deleting partition!\n";
+                     neverSaveData = 1;
+                  } else saveData = 1;
+                                                      break;
+               case 'D':
+                  cout << GetAlignment() << "\n";
+                  break;
+               case 'e':
+                  JustLooking(0);
+                  MoveSecondHeaderToEnd();
+                  saveData = 1;
+                  break;
+               case 'E':
+                  cout << FindLastInFree(FindFirstInLargest()) << "\n";
+                  break;
+               case 'f':
+                  cout << FindFirstInLargest() << "\n";
+                  break;
+               case 'F':
+                  temp = FindFirstInLargest();
+                  Align(&temp);
+                  cout << temp << "\n";
+                  break;
+               case 'g':
+                  JustLooking(0);
+                  saveData = 1;
+                  saveNonGPT = 1;
+                  break;
+               case 'G':
+                  JustLooking(0);
+                  saveData = 1;
+                  RandomizeGUIDs();
+                  break;
+               case 'h':
+                  JustLooking(0);
+                  if (BuildMBR(hybrids, 1) == 1)
+                     saveData = 1;
+                  break;
+               case 'i':
+                  ShowPartDetails(infoPartNum - 1);
+                  break;
+               case 'l':
+                  LoadBackupFile(backupFile, saveData, neverSaveData);
+                  free(backupFile);
+                  break;
+               case 'L':
+                  break;
+               case 'm':
+                  JustLooking(0);
+                  if (BuildMBR(mbrParts, 0) == 1) {
+                     if (!pretend) {
+                        if (SaveMBR()) {
+                           DestroyGPT();
+                        } else
+                           cerr << "Problem saving MBR!\n";
+                     } // if
+                     saveNonGPT = 0;
+                     pretend = 1; // Not really, but works around problem if -g is used with this...
+                     saveData = 0;
+                  } // if
+                  break;
+               case 'n':
+                  JustLooking(0);
+                  newPartNum = (int) GetInt(newPartInfo, 1) - 1;
+                  if (newPartNum < 0)
+                     newPartNum = FindFirstFreePart();
+                  low = FindFirstInLargest();
+                  Align(&low);
+                  high = FindLastInFree(low);
+                  startSector = IeeeToInt(GetString(newPartInfo, 2), sSize, low, high, low);
+                  endSector = IeeeToInt(GetString(newPartInfo, 3), sSize, startSector, high, high);
+                  if (CreatePartition(newPartNum, startSector, endSector)) {
+                     saveData = 1;
+                  } else {
+                     cerr << "Could not create partition " << newPartNum + 1 << " from "
+                          << startSector << " to " << endSector << "\n";
+                     neverSaveData = 1;
+                  } // if/else
+                  free(newPartInfo);
+                  break;
+               case 'N':
+                  JustLooking(0);
+                  startSector = FindFirstInLargest();
+                  Align(&startSector);
+                  endSector = FindLastInFree(startSector);
+                  if (largestPartNum <= 0)
+                     largestPartNum = FindFirstFreePart() + 1;
+                  if (CreatePartition(largestPartNum - 1, startSector, endSector)) {
+                     saveData = 1;
+                  } else {
+                     cerr << "Could not create partition " << largestPartNum << " from "
+                     << startSector << " to " << endSector << "\n";
+                     neverSaveData = 1;
+                  } // if/else
+                  break;
+               case 'o':
+                  JustLooking(0);
+                  ClearGPTData();
+                  saveData = 1;
+                  break;
+               case 'O':
+                   DisplayMBRData();
+                   break;
+               case 'p':
+                  DisplayGPTData();
+                  break;
+               case 'P':
+                  pretend = 1;
+                  break;
+               case 'r':
+                  JustLooking(0);
+                  uint64_t p1, p2;
+                  p1 = GetInt(twoParts, 1) - 1;
+                  p2 = GetInt(twoParts, 2) - 1;
+                  if (SwapPartitions((uint32_t) p1, (uint32_t) p2) == 0) {
+                     neverSaveData = 1;
+                     cerr << "Cannot swap partitions " << p1 + 1 << " and " << p2 + 1 << "\n";
+                  } else saveData = 1;
+                                                      break;
+               case 'R':
+                  secondDevice = *this;
+                  secondDevice.SetDisk(outDevice);
+                  secondDevice.JustLooking(0);
+                  if (!secondDevice.SaveGPTData(1))
+                     retval = 8;
+                  break;
+               case 's':
+                  JustLooking(0);
+                  SortGPT();
+                  saveData = 1;
+                  break;
+               case 'S':
+                  JustLooking(0);
+                  if (SetGPTSize(tableSize) == 0)
+                     neverSaveData = 1;
+                  else
+                     saveData = 1;
+                  break;
+               case 't':
+                  JustLooking(0);
+                  partNum = (int) GetInt(typeCode, 1) - 1;
+                  if (partNum < 0)
+                     partNum = newPartNum;
+                  if ((partNum >= 0) && (partNum < (int) GetNumParts())) {
+                     typeHelper = GetString(typeCode, 2);
+                     if ((typeHelper != (GUIDData) "00000000-0000-0000-0000-000000000000") &&
+                         (ChangePartType(partNum, typeHelper))) {
+                        saveData = 1;
+                        } else {
+                           cerr << "Could not change partition " << partNum + 1
+                           << "'s type code to " << GetString(typeCode, 2) << "!\n";
+                           neverSaveData = 1;
+                        } // if/else
+                     free(typeCode);
+                  }
+                  break;
+               case 'T':
+                  JustLooking(0);
+                  XFormDisklabel(bsdPartNum - 1);
+                  saveData = 1;
+                  break;
+               case 'u':
+                  JustLooking(0);
+                  saveData = 1;
+                  partNum = (int) GetInt(partGUID, 1) - 1;
+                  if (partNum < 0)
+                     partNum = newPartNum;
+                  if ((partNum >= 0) && (partNum < (int) GetNumParts())) {
+                     SetPartitionGUID(partNum, GetString(partGUID, 2).c_str());
+                  }
+                  break;
+               case 'U':
+                  JustLooking(0);
+                  saveData = 1;
+                  SetDiskGUID(diskGUID);
+                  break;
+               case 'v':
+                  Verify();
+                  break;
+               case 'z':
+                  if (!pretend) {
+                     DestroyGPT();
+                  } // if
+                  saveNonGPT = 1;
+                  saveData = 0;
+                  break;
+               case 'Z':
+                  if (!pretend) {
+                     DestroyGPT();
+                     DestroyMBR();
+                  } // if
+                  saveNonGPT = 1;
+                  saveData = 0;
+                  break;
+               default:
+                  cerr << "Unknown option (-" << opt << ")!\n";
+                  break;
+               } // switch
+         } // while
+      } else { // if loaded OK
+         poptResetContext(poptCon);
+         // Do a few types of operations even if there are problems....
+         while ((opt = poptGetNextOpt(poptCon)) > 0) {
+            switch (opt) {
+               case 'l':
+                  LoadBackupFile(backupFile, saveData, neverSaveData);
+                  cout << "Information: Loading backup partition table; will override earlier problems!\n";
+                  free(backupFile);
+                  retval = 0;
+                  break;
+               case 'o':
+                  JustLooking(0);
+                  ClearGPTData();
+                  saveData = 1;
+                  cout << "Information: Creating fresh partition table; will override earlier problems!\n";
+                  retval = 0;
+                  break;
+               case 'v':
+                  cout << "Verification may miss some problems or report too many!\n";
+                  Verify();
+                  break;
+               case 'z':
+                  if (!pretend) {
+                     DestroyGPT();
+                  } // if
+                  saveNonGPT = 1;
+                  saveData = 0;
+                  break;
+               case 'Z':
+                  if (!pretend) {
+                     DestroyGPT();
+                     DestroyMBR();
+                  } // if
+                  saveNonGPT = 1;
+                  saveData = 0;
+                  break;
+            } // switch
+         } // while
+         retval = 2;
+      } // if/else loaded OK
+      if ((saveData) && (!neverSaveData) && (saveNonGPT) && (!pretend)) {
+         if (!SaveGPTData(1))
+            retval = 4;
+      }
+      if (saveData && (!saveNonGPT)) {
+         cout << "Non-GPT disk; not saving changes. Use -g to override.\n";
+         retval = 3;
+      } // if
+      if (neverSaveData) {
+         cerr << "Error encountered; not saving changes.\n";
+         retval = 4;
+      } // if
+   } // if (device != NULL)
+   poptFreeContext(poptCon);
+   return retval;
+} // GPTDataCL::DoOptions()
+
+// Create a hybrid or regular MBR from GPT data structures
+int GPTDataCL::BuildMBR(char* argument, int isHybrid) {
+   int numParts, allOK = 1, i, origPartNum;
+   MBRPart newPart;
+   BasicMBRData newMBR;
+
+   if (argument != NULL) {
+      numParts = CountColons(argument) + 1;
+      if (numParts <= (4 - isHybrid)) {
+         newMBR.SetDisk(GetDisk());
+         for (i = 0; i < numParts; i++) {
+            origPartNum = GetInt(argument, i + 1) - 1;
+            if (IsUsedPartNum(origPartNum) && (partitions[origPartNum].IsSizedForMBR() == MBR_SIZED_GOOD)) {
+               newPart.SetInclusion(PRIMARY);
+               newPart.SetLocation(operator[](origPartNum).GetFirstLBA(),
+                                   operator[](origPartNum).GetLengthLBA());
+               newPart.SetStatus(0);
+               newPart.SetType((uint8_t)(operator[](origPartNum).GetHexType() / 0x0100));
+               newMBR.AddPart(i + isHybrid, newPart);
+            } else {
+               cerr << "Original partition " << origPartNum + 1 << " does not exist or is too big! Aborting operation!\n";
+               allOK = 0;
+            } // if/else
+         } // for
+         if (isHybrid) {
+            newPart.SetInclusion(PRIMARY);
+            newPart.SetLocation(1, newMBR.FindLastInFree(1));
+            newPart.SetStatus(0);
+            newPart.SetType(0xEE);
+            newMBR.AddPart(0, newPart);
+         } // if
+         if (allOK)
+            SetProtectiveMBR(newMBR);
+      } else allOK = 0;
+   } else allOK = 0;
+   if (!allOK)
+      cerr << "Problem creating MBR!\n";
+   return allOK;
+} // GPTDataCL::BuildMBR()
+
+// Returns the number of colons in argument string, ignoring the
+// first character (thus, a leading colon is ignored, as GetString()
+// does).
+int CountColons(char* argument) {
+   int num = 0;
+
+   while ((argument[0] != '\0') && (argument = strchr(&argument[1], ':')))
+      num++;
+
+   return num;
+} // GPTDataCL::CountColons()
+
+// Extract integer data from argument string, which should be colon-delimited
+uint64_t GetInt(const string & argument, int itemNum) {
+   uint64_t retval;
+
+   istringstream inString(GetString(argument, itemNum));
+   inString >> retval;
+   return retval;
+} // GPTDataCL::GetInt()
+
+// Extract string data from argument string, which should be colon-delimited
+// If string begins with a colon, that colon is skipped in the counting. If an
+// invalid itemNum is specified, returns an empty string.
+string GetString(string argument, int itemNum) {
+   size_t startPos = 0, endPos = 0;
+   string retVal = "";
+   int foundLast = 0;
+   int numFound = 0;
+
+   if (argument[0] == ':')
+      argument.erase(0, 1);
+   while ((numFound < itemNum) && (!foundLast)) {
+      endPos = argument.find(':', startPos);
+      numFound++;
+      if (endPos == string::npos) {
+         foundLast = 1;
+         endPos = argument.length();
+      } else if (numFound < itemNum) {
+         startPos = endPos + 1;
+      } // if/elseif
+   } // while
+   if ((numFound == itemNum) && (numFound > 0))
+      retVal = argument.substr(startPos, endPos - startPos);
+
+   return retVal;
+} // GetString()
diff --git a/gptfdisk/gptcl.h b/gptfdisk/gptcl.h
new file mode 100644
index 0000000..610ca5f
--- /dev/null
+++ b/gptfdisk/gptcl.h
@@ -0,0 +1,53 @@
+/*
+    Implementation of GPTData class derivative with popt-based command
+    line processing
+    Copyright (C) 2010-2013 Roderick W. Smith
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+
+#ifndef __GPTCL_H
+#define __GPTCL_H
+
+#include "gpt.h"
+#include <popt.h>
+
+using namespace std;
+
+class GPTDataCL : public GPTData {
+   protected:
+      // Following are variables associated with popt parameters....
+      char *attributeOperation, *backupFile, *partName, *hybrids;
+      char *newPartInfo, *mbrParts, *twoParts, *outDevice, *typeCode;
+      char *partGUID, *diskGUID;
+      int alignment, deletePartNum, infoPartNum, largestPartNum, bsdPartNum;
+      uint32_t tableSize;
+      poptContext poptCon;
+
+      int BuildMBR(char* argument, int isHybrid);
+   public:
+      GPTDataCL(void);
+      GPTDataCL(string filename);
+      ~GPTDataCL(void);
+      void LoadBackupFile(string backupFile, int &saveData, int &neverSaveData);
+      int DoOptions(int argc, char* argv[]);
+}; // class GPTDataCL
+
+int CountColons(char* argument);
+uint64_t GetInt(const string & argument, int itemNum);
+string GetString(string argument, int itemNum);
+
+#endif
diff --git a/gptfdisk/gptcurses.cc b/gptfdisk/gptcurses.cc
new file mode 100644
index 0000000..0e18f8f
--- /dev/null
+++ b/gptfdisk/gptcurses.cc
@@ -0,0 +1,821 @@
+/*
+ *    Implementation of GPTData class derivative with curses-based text-mode
+ *    interaction
+ *    Copyright (C) 2011-2013 Roderick W. Smith
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <iostream>
+#include <string>
+#include <sstream>
+#include <ncurses.h>
+#include "gptcurses.h"
+#include "support.h"
+
+using namespace std;
+
+// # of lines to reserve for general information and headers (RESERVED_TOP)
+// and for options and messages (RESERVED_BOTTOM)
+#define RESERVED_TOP 7
+#define RESERVED_BOTTOM 5
+
+int GPTDataCurses::numInstances = 0;
+
+GPTDataCurses::GPTDataCurses(void) {
+   if (numInstances > 0) {
+      refresh();
+   } else {
+      setlocale( LC_ALL , "" );
+      initscr();
+      cbreak();
+      noecho();
+      intrflush(stdscr, false);
+      keypad(stdscr, true);
+      nonl();
+      numInstances++;
+   } // if/else
+   firstSpace = NULL;
+   lastSpace = NULL;
+   currentSpace = NULL;
+   currentSpaceNum = -1;
+   whichOptions = ""; // current set of options
+   currentKey = 'b'; // currently selected option
+   displayType = USE_CURSES;
+} // GPTDataCurses constructor
+
+GPTDataCurses::~GPTDataCurses(void) {
+   numInstances--;
+   if ((numInstances == 0) && !isendwin())
+      endwin();
+} // GPTDataCurses destructor
+
+/************************************************
+ *                                              *
+ * Functions relating to Spaces data structures *
+ *                                              *
+ ************************************************/
+
+void GPTDataCurses::EmptySpaces(void) {
+   Space *trash;
+
+   while (firstSpace != NULL) {
+      trash = firstSpace;
+      firstSpace = firstSpace->nextSpace;
+      delete trash;
+   } // if
+   numSpaces = 0;
+   lastSpace = NULL;
+} // GPTDataCurses::EmptySpaces()
+
+// Create Spaces from partitions. Does NOT creates Spaces to represent
+// unpartitioned space on the disk.
+// Returns the number of Spaces created.
+int GPTDataCurses::MakeSpacesFromParts(void) {
+   uint i;
+   Space *tempSpace;
+
+   EmptySpaces();
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) {
+         tempSpace = new Space;
+         tempSpace->firstLBA = partitions[i].GetFirstLBA();
+         tempSpace->lastLBA = partitions[i].GetLastLBA();
+         tempSpace->origPart = &partitions[i];
+         tempSpace->partNum = (int) i;
+         LinkToEnd(tempSpace);
+      } // if
+   } // for
+   return numSpaces;
+} // GPTDataCurses::MakeSpacesFromParts()
+
+// Add a single empty Space to the current Spaces linked list and sort the result....
+void GPTDataCurses::AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA) {
+   Space *tempSpace;
+
+   tempSpace = new Space;
+   tempSpace->firstLBA = firstLBA;
+   tempSpace->lastLBA = lastLBA;
+   tempSpace->origPart = &emptySpace;
+   tempSpace->partNum = -1;
+   LinkToEnd(tempSpace);
+   SortSpaces();
+} // GPTDataCurses::AddEmptySpace();
+
+// Add Spaces to represent the unallocated parts of the partition table.
+// Returns the number of Spaces added.
+int GPTDataCurses::AddEmptySpaces(void) {
+   int numAdded = 0;
+   Space *current;
+
+   SortSpaces();
+   if (firstSpace == NULL) {
+      AddEmptySpace(GetFirstUsableLBA(), GetLastUsableLBA());
+      numAdded++;
+   } else {
+      current = firstSpace;
+      while ((current != NULL) /* && (current->partNum != -1) */ ) {
+         if ((current == firstSpace) && (current->firstLBA > GetFirstUsableLBA())) {
+            AddEmptySpace(GetFirstUsableLBA(), current->firstLBA - 1);
+            numAdded++;
+         } // if
+         if ((current == lastSpace) && (current->lastLBA < GetLastUsableLBA())) {
+            AddEmptySpace(current->lastLBA + 1, GetLastUsableLBA());
+            numAdded++;
+         } // if
+         if ((current->prevSpace != NULL) && (current->prevSpace->lastLBA < (current->firstLBA - 1))) {
+            AddEmptySpace(current->prevSpace->lastLBA + 1, current->firstLBA - 1);
+            numAdded++;
+         } // if
+         current = current->nextSpace;
+      } // while
+   } // if/else
+   return numAdded;
+} // GPTDataCurses::AddEmptySpaces()
+
+// Remove the specified Space from the linked list and set its previous and
+// next pointers to NULL.
+void GPTDataCurses::UnlinkSpace(Space *theSpace) {
+   if (theSpace != NULL) {
+      if (theSpace->prevSpace != NULL)
+         theSpace->prevSpace->nextSpace = theSpace->nextSpace;
+      if (theSpace->nextSpace != NULL)
+         theSpace->nextSpace->prevSpace = theSpace->prevSpace;
+      if (theSpace == firstSpace)
+         firstSpace = theSpace->nextSpace;
+      if (theSpace == lastSpace)
+         lastSpace = theSpace->prevSpace;
+      theSpace->nextSpace = NULL;
+      theSpace->prevSpace = NULL;
+      numSpaces--;
+   } // if
+} // GPTDataCurses::UnlinkSpace
+
+// Link theSpace to the end of the current linked list.
+void GPTDataCurses::LinkToEnd(Space *theSpace) {
+   if (lastSpace == NULL) {
+      firstSpace = lastSpace = theSpace;
+      theSpace->nextSpace = NULL;
+      theSpace->prevSpace = NULL;
+   } else {
+      theSpace->prevSpace = lastSpace;
+      theSpace->nextSpace = NULL;
+      lastSpace->nextSpace = theSpace;
+      lastSpace = theSpace;
+   } // if/else
+   numSpaces++;
+} // GPTDataCurses::LinkToEnd()
+
+// Sort spaces into ascending order by on-disk position.
+void GPTDataCurses::SortSpaces(void) {
+   Space *oldFirst, *oldLast, *earliest = NULL, *current = NULL;
+
+   oldFirst = firstSpace;
+   oldLast = lastSpace;
+   firstSpace = lastSpace = NULL;
+   while (oldFirst != NULL) {
+      current = earliest = oldFirst;
+      while (current != NULL) {
+         if (current->firstLBA < earliest->firstLBA)
+            earliest = current;
+         current = current->nextSpace;
+      } // while
+      if (oldFirst == earliest)
+         oldFirst = earliest->nextSpace;
+      if (oldLast == earliest)
+         oldLast = earliest->prevSpace;
+      UnlinkSpace(earliest);
+      LinkToEnd(earliest);
+   } // while
+} // GPTDataCurses::SortSpaces()
+
+// Identify the spaces on the disk, a "space" being defined as a partition
+// or an empty gap between, before, or after partitions. The spaces are
+// presented to users in the main menu display.
+void GPTDataCurses::IdentifySpaces(void) {
+   MakeSpacesFromParts();
+   AddEmptySpaces();
+} // GPTDataCurses::IdentifySpaces()
+
+/**************************
+ *                        *
+ * Data display functions *
+ *                        *
+ **************************/
+
+// Display a single Space on line # lineNum.
+// Returns a pointer to the space being displayed
+Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) {
+   Space *space;
+   int i = 0;
+#ifdef USE_UTF16
+   char temp[40];
+#endif
+
+   space = firstSpace;
+   while ((space != NULL) && (i < spaceNum)) {
+      space = space->nextSpace;
+      i++;
+   } // while
+   if ((space != NULL) && (lineNum < (LINES - 5))) {
+      ClearLine(lineNum);
+      if (space->partNum == -1) { // space is empty
+         move(lineNum, 12);
+         printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
+         move(lineNum, 24);
+         printw("free space");
+      } else { // space holds a partition
+         move(lineNum, 3);
+         printw("%d", space->partNum + 1);
+         move(lineNum, 12);
+         printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
+         move(lineNum, 24);
+         printw(space->origPart->GetTypeName().c_str());
+         move(lineNum, 50);
+         #ifdef USE_UTF16
+         space->origPart->GetDescription().extract(0, 39, temp, 39);
+         printw(temp);
+         #else
+         printw(space->origPart->GetDescription().c_str());
+         #endif
+      } // if/else
+   } // if
+   return space;
+} // GPTDataCurses::ShowSpace
+
+// Display the partitions, being sure that the space #selected is displayed
+// and highlighting that space.
+// Returns the number of the space being shown (should be selected, but will
+// be -1 if something weird happens)
+int GPTDataCurses::DisplayParts(int selected) {
+   int lineNum = 5, i = 0, retval = -1, numToShow, pageNum;
+   string theLine;
+
+   move(lineNum++, 0);
+   theLine = "Part. #     Size        Partition Type            Partition Name";
+   printw(theLine.c_str());
+   move(lineNum++, 0);
+   theLine = "----------------------------------------------------------------";
+   printw(theLine.c_str());
+   numToShow = LINES - RESERVED_TOP - RESERVED_BOTTOM;
+   pageNum = selected / numToShow;
+   for (i = pageNum * numToShow; i <= (pageNum + 1) * numToShow - 1; i++) {
+      if (i < numSpaces) { // real space; show it
+         if (i == selected) {
+            currentSpaceNum = i;
+            if (displayType == USE_CURSES) {
+               attron(A_REVERSE);
+               currentSpace = ShowSpace(i, lineNum++);
+               attroff(A_REVERSE);
+            } else {
+               currentSpace = ShowSpace(i, lineNum);
+               move(lineNum++, 0);
+               printw(">");
+            }
+            DisplayOptions(i);
+            retval = selected;
+         } else {
+            ShowSpace(i, lineNum++);
+         }
+      } else { // blank in display
+         ClearLine(lineNum++);
+      } // if/else
+   } // for
+   refresh();
+   return retval;
+} // GPTDataCurses::DisplayParts()
+
+/**********************************************
+ *                                            *
+ * Functions corresponding to main menu items *
+ *                                            *
+ **********************************************/
+
+// Delete the specified partition and re-detect partitions and spaces....
+void GPTDataCurses::DeletePartition(int partNum) {
+   if (!GPTData::DeletePartition(partNum))
+      Report("Could not delete partition!");
+   IdentifySpaces();
+   if (currentSpaceNum >= numSpaces) {
+      currentSpaceNum = numSpaces - 1;
+      currentSpace = lastSpace;
+   } // if
+} // GPTDataCurses::DeletePartition()
+
+// Displays information on the specified partition
+void GPTDataCurses::ShowInfo(int partNum) {
+   uint64_t size;
+#ifdef USE_UTF16
+   char temp[NAME_SIZE + 1];
+#endif
+
+   clear();
+   move(2, (COLS - 29) / 2);
+   printw("Information for partition #%d\n\n", partNum + 1);
+   printw("Partition GUID code: %s (%s)\n", partitions[partNum].GetType().AsString().c_str(),
+          partitions[partNum].GetTypeName().c_str());
+   printw("Partition unique GUID: %s\n", partitions[partNum].GetUniqueGUID().AsString().c_str());
+   printw("First sector: %lld (at %s)\n", partitions[partNum].GetFirstLBA(),
+          BytesToIeee(partitions[partNum].GetFirstLBA(), blockSize).c_str());
+   printw("Last sector: %lld (at %s)\n", partitions[partNum].GetLastLBA(),
+          BytesToIeee(partitions[partNum].GetLastLBA(), blockSize).c_str());
+   size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA() + 1;
+   printw("Partition size: %lld sectors (%s)\n", size, BytesToIeee(size, blockSize).c_str());
+   printw("Attribute flags: %016x\n", partitions[partNum].GetAttributes().GetAttributes());
+   #ifdef USE_UTF16
+   partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE );
+   printw("Partition name: '%s'\n", temp);
+   #else
+   printw("Partition name: '%s'\n", partitions[partNum].GetDescription().c_str());
+   #endif
+   PromptToContinue();
+} // GPTDataCurses::ShowInfo()
+
+// Prompt for and change a partition's name....
+void GPTDataCurses::ChangeName(int partNum) {
+   char temp[NAME_SIZE + 1];
+
+   if (ValidPartNum(partNum)) {
+      move(LINES - 4, 0);
+      clrtobot();
+      move(LINES - 4, 0);
+      #ifdef USE_UTF16
+      partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE );
+      printw("Current partition name is '%s'\n", temp);
+      #else
+      printw("Current partition name is '%s'\n", partitions[partNum].GetDescription().c_str());
+      #endif
+      printw("Enter new partition name, or <Enter> to use the current name:\n");
+      echo();
+      getnstr(temp, NAME_SIZE );
+      partitions[partNum].SetName((string) temp);
+      noecho();
+   } // if
+} // GPTDataCurses::ChangeName()
+
+// Change the partition's type code....
+void GPTDataCurses::ChangeType(int partNum) {
+   char temp[80] = "L\0";
+   PartType tempType;
+
+   echo();
+   do {
+      move(LINES - 4, 0);
+      clrtobot();
+      move(LINES - 4, 0);
+      printw("Current type is %04x (%s)\n", partitions[partNum].GetType().GetHexType(), partitions[partNum].GetTypeName().c_str());
+      printw("Hex code or GUID (L to show codes, Enter = %04x): ", partitions[partNum].GetType().GetHexType());
+      getnstr(temp, 79);
+      if ((temp[0] == 'L') || (temp[0] == 'l')) {
+         ShowTypes();
+      } else {
+         if (temp[0] == '\0')
+            tempType = partitions[partNum].GetType().GetHexType();
+         tempType = temp;
+         partitions[partNum].SetType(tempType);
+      } // if
+   } while ((temp[0] == 'L') || (temp[0] == 'l') || (partitions[partNum].GetType() == (GUIDData) "0x0000"));
+   noecho();
+} // GPTDataCurses::ChangeType
+
+// Sets the partition alignment value
+void GPTDataCurses::SetAlignment(void) {
+   int alignment;
+
+   move(LINES - 4, 0);
+   clrtobot();
+   printw("Current partition alignment, in sectors, is %d.", GetAlignment());
+   do {
+      move(LINES - 3, 0);
+      printw("Type new alignment value, in sectors: ");
+      echo();
+      scanw("%d", &alignment);
+      noecho();
+   } while ((alignment == 0) || (alignment > MAX_ALIGNMENT));
+   GPTData::SetAlignment(alignment);
+} // GPTDataCurses::SetAlignment()
+
+// Verify the data structures. Note that this function leaves curses mode and
+// relies on the underlying GPTData::Verify() function to report on problems
+void GPTDataCurses::Verify(void) {
+   char junk;
+
+   def_prog_mode();
+   endwin();
+   GPTData::Verify();
+   cout << "\nPress the <Enter> key to continue: ";
+   cin.get(junk);
+   reset_prog_mode();
+   refresh();
+} // GPTDataCurses::Verify()
+
+// Create a new partition in the space pointed to by currentSpace.
+void GPTDataCurses::MakeNewPart(void) {
+   uint64_t size, newFirstLBA = 0, newLastLBA = 0;
+   int partNum;
+   char inLine[80];
+
+   move(LINES - 4, 0);
+   clrtobot();
+   while ((newFirstLBA < currentSpace->firstLBA) || (newFirstLBA > currentSpace->lastLBA)) {
+      newFirstLBA = currentSpace->firstLBA;
+      move(LINES - 4, 0);
+      clrtoeol();
+      newFirstLBA = currentSpace->firstLBA;
+      Align(&newFirstLBA);
+      printw("First sector (%lld-%lld, default = %lld): ", newFirstLBA, currentSpace->lastLBA, newFirstLBA);
+      echo();
+      getnstr(inLine, 79);
+      noecho();
+      newFirstLBA = IeeeToInt(inLine, blockSize, currentSpace->firstLBA, currentSpace->lastLBA, newFirstLBA);
+      Align(&newFirstLBA);
+   } // while
+   size = currentSpace->lastLBA - newFirstLBA + 1;
+   while ((newLastLBA > currentSpace->lastLBA) || (newLastLBA < newFirstLBA)) {
+      move(LINES - 3, 0);
+      clrtoeol();
+      printw("Size in sectors or {KMGTP} (default = %lld): ", size);
+      echo();
+      getnstr(inLine, 79);
+      noecho();
+      newLastLBA = newFirstLBA + IeeeToInt(inLine, blockSize, 1, size, size) - 1;
+   } // while
+   partNum = FindFirstFreePart();
+   if (CreatePartition(partNum, newFirstLBA, newLastLBA)) { // created OK; set type code & name....
+      ChangeType(partNum);
+      ChangeName(partNum);
+   } else {
+      Report("Error creating partition!");
+   } // if/else
+} // GPTDataCurses::MakeNewPart()
+
+// Prompt user for permission to save data and, if it's given, do so!
+void GPTDataCurses::SaveData(void) {
+   string answer = "";
+   char inLine[80];
+
+   move(LINES - 4, 0);
+   clrtobot();
+   move (LINES - 2, 14);
+   printw("Warning!! This may destroy data on your disk!");
+   echo();
+   while ((answer != "yes") && (answer != "no")) {
+      move (LINES - 4, 2);
+      printw("Are you sure you want to write the partition table to disk? (yes or no): ");
+      getnstr(inLine, 79);
+      answer = inLine;
+      if ((answer != "yes") && (answer != "no")) {
+         move(LINES - 2, 0);
+         clrtoeol();
+         move(LINES - 2, 14);
+         printw("Please enter 'yes' or 'no'");
+      } // if
+   } // while()
+   noecho();
+   if (answer == "yes") {
+      if (SaveGPTData(1)) {
+         if (!myDisk.DiskSync())
+            Report("The kernel may be using the old partition table. Reboot to use the new\npartition table!");
+      } else {
+         Report("Problem saving data! Your partition table may be damaged!");
+      }
+   }
+} // GPTDataCurses::SaveData()
+
+// Back up the partition table, prompting user for a filename....
+void GPTDataCurses::Backup(void) {
+   char inLine[80];
+
+   ClearBottom();
+   move(LINES - 3, 0);
+   printw("Enter backup filename to save: ");
+   echo();
+   getnstr(inLine, 79);
+   noecho();
+   SaveGPTBackup(inLine);
+} // GPTDataCurses::Backup()
+
+// Load a GPT backup from a file
+void GPTDataCurses::LoadBackup(void) {
+   char inLine[80];
+
+   ClearBottom();
+   move(LINES - 3, 0);
+   printw("Enter backup filename to load: ");
+   echo();
+   getnstr(inLine, 79);
+   noecho();
+   if (!LoadGPTBackup(inLine))
+      Report("Restoration failed!");
+   IdentifySpaces();
+} // GPTDataCurses::LoadBackup()
+
+// Display some basic help information
+void GPTDataCurses::ShowHelp(void) {
+   int i = 0;
+
+   clear();
+   move(0, (COLS - 22) / 2);
+   printw("Help screen for cgdisk");
+   move(2, 0);
+   printw("This is cgdisk, a curses-based disk partitioning program. You can use it\n");
+   printw("to create, delete, and modify partitions on your hard disk.\n\n");
+   attron(A_BOLD);
+   printw("Use cgdisk only on GUID Partition Table (GPT) disks!\n");
+   attroff(A_BOLD);
+   printw("Use cfdisk on Master Boot Record (MBR) disks.\n\n");
+   printw("Command      Meaning\n");
+   printw("-------      -------\n");
+   while (menuMain[i].key != 0) {
+      printw("   %c         %s\n", menuMain[i].key, menuMain[i].desc.c_str());
+      i++;
+   } // while()
+   PromptToContinue();
+} // GPTDataCurses::ShowHelp()
+
+/************************************
+ *                                  *
+ * User input and menuing functions *
+ *                                  *
+ ************************************/
+
+// Change the currently-selected space....
+void GPTDataCurses::ChangeSpaceSelection(int delta) {
+   if (currentSpace != NULL) {
+      while ((delta > 0) && (currentSpace->nextSpace != NULL)) {
+         currentSpace = currentSpace->nextSpace;
+         delta--;
+         currentSpaceNum++;
+      } // while
+      while ((delta < 0) && (currentSpace->prevSpace != NULL)) {
+         currentSpace = currentSpace->prevSpace;
+         delta++;
+         currentSpaceNum--;
+      } // while
+   } // if
+   // Below will hopefully never be true; bad counting error (bug), so reset to
+   // the first Space as a failsafe....
+   if (DisplayParts(currentSpaceNum) != currentSpaceNum) {
+      currentSpaceNum = 0;
+      currentSpace = firstSpace;
+      DisplayParts(currentSpaceNum);
+   } // if
+} // GPTDataCurses
+
+// Move option selection left or right....
+void GPTDataCurses::MoveSelection(int delta) {
+   int newKeyNum;
+
+   // Begin with a sanity check to ensure a valid key is selected....
+   if (whichOptions.find(currentKey) == string::npos)
+      currentKey = 'n';
+   newKeyNum = whichOptions.find(currentKey);
+   newKeyNum += delta;
+   if (newKeyNum < 0)
+      newKeyNum = whichOptions.length() - 1;
+   newKeyNum %= whichOptions.length();
+   currentKey = whichOptions[newKeyNum];
+   DisplayOptions(currentKey);
+} // GPTDataCurses::MoveSelection()
+
+// Show user's options. Refers to currentSpace to determine which options to show.
+// Highlights the option with the key selectedKey; or a default if that's invalid.
+void GPTDataCurses::DisplayOptions(char selectedKey) {
+   uint i, j = 0, firstLine, numPerLine;
+   string optionName, optionDesc = "";
+
+   if (currentSpace != NULL) {
+      if (currentSpace->partNum == -1) { // empty space is selected
+         whichOptions = EMPTY_SPACE_OPTIONS;
+         if (whichOptions.find(selectedKey) == string::npos)
+            selectedKey = 'n';
+      } else { // a partition is selected
+         whichOptions = PARTITION_OPTIONS;
+         if (whichOptions.find(selectedKey) == string::npos)
+            selectedKey = 't';
+      } // if/else
+
+      firstLine = LINES - 4;
+      numPerLine = (COLS - 8) / 12;
+      ClearBottom();
+      move(firstLine, 0);
+      for (i = 0; i < whichOptions.length(); i++) {
+         optionName = "";
+         for (j = 0; menuMain[j].key; j++) {
+            if (menuMain[j].key == whichOptions[i]) {
+               optionName = menuMain[j].name;
+               if (whichOptions[i] == selectedKey)
+                  optionDesc = menuMain[j].desc;
+            } // if
+         } // for
+         move(firstLine + i / numPerLine, (i % numPerLine) * 12 + 4);
+         if (whichOptions[i] == selectedKey) {
+            attron(A_REVERSE);
+            printw("[ %s ]", optionName.c_str());
+            attroff(A_REVERSE);
+         } else {
+            printw("[ %s ]", optionName.c_str());
+         } // if/else
+      } // for
+      move(LINES - 1, (COLS - optionDesc.length()) / 2);
+      printw(optionDesc.c_str());
+      currentKey = selectedKey;
+   } // if
+} // GPTDataCurses::DisplayOptions()
+
+// Accept user input and process it. Returns when the program should terminate.
+void GPTDataCurses::AcceptInput() {
+   int inputKey, exitNow = 0;
+
+   do {
+      refresh();
+      inputKey = getch();
+      switch (inputKey) {
+         case KEY_UP:
+            ChangeSpaceSelection(-1);
+            break;
+         case KEY_DOWN:
+            ChangeSpaceSelection(+1);
+            break;
+         case 339: // page up key
+            ChangeSpaceSelection(RESERVED_TOP + RESERVED_BOTTOM - LINES);
+            break;
+         case 338: // page down key
+            ChangeSpaceSelection(LINES - RESERVED_TOP - RESERVED_BOTTOM);
+            break;
+         case KEY_LEFT:
+            MoveSelection(-1);
+            break;
+         case KEY_RIGHT:
+            MoveSelection(+1);
+            break;
+         case KEY_ENTER: case 13:
+            exitNow = Dispatch(currentKey);
+            break;
+         case 27: // escape key
+            exitNow = 1;
+            break;
+         default:
+            exitNow = Dispatch(inputKey);
+            break;
+      } // switch()
+   } while (!exitNow);
+} // GPTDataCurses::AcceptInput()
+
+// Operation has been selected, so do it. Returns 1 if the program should
+// terminate on return from this program, 0 otherwise.
+int GPTDataCurses::Dispatch(char operation) {
+   int exitNow = 0;
+
+   switch (operation) {
+      case 'a': case 'A':
+         SetAlignment();
+         break;
+      case 'b': case 'B':
+         Backup();
+         break;
+      case 'd': case 'D':
+         if (ValidPartNum(currentSpace->partNum))
+            DeletePartition(currentSpace->partNum);
+         break;
+      case 'h': case 'H':
+         ShowHelp();
+         break;
+      case 'i': case 'I':
+         if (ValidPartNum(currentSpace->partNum))
+            ShowInfo(currentSpace->partNum);
+         break;
+      case 'l': case 'L':
+         LoadBackup();
+         break;
+      case 'm': case 'M':
+         if (ValidPartNum(currentSpace->partNum))
+            ChangeName(currentSpace->partNum);
+         break;
+      case 'n': case 'N':
+         if (currentSpace->partNum < 0) {
+            MakeNewPart();
+            IdentifySpaces();
+         } // if
+         break;
+      case 'q': case 'Q':
+         exitNow = 1;
+         break;
+      case 't': case 'T':
+         if (ValidPartNum(currentSpace->partNum))
+            ChangeType(currentSpace->partNum);
+         break;
+      case 'v': case 'V':
+         Verify();
+         break;
+      case 'w': case 'W':
+         SaveData();
+         break;
+      default:
+         break;
+   } // switch()
+   DrawMenu();
+   return exitNow;
+} // GPTDataCurses::Dispatch()
+
+// Draws the main menu
+void GPTDataCurses::DrawMenu(void) {
+   string title="cgdisk ";
+   title += GPTFDISK_VERSION;
+   string drive="Disk Drive: ";
+   drive += device;
+   ostringstream size;
+
+   size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize);
+
+   clear();
+   move(0, (COLS - title.length()) / 2);
+   printw(title.c_str());
+   move(2, (COLS - drive.length()) / 2);
+   printw(drive.c_str());
+   move(3, (COLS - size.str().length()) / 2);
+   printw(size.str().c_str());
+   DisplayParts(currentSpaceNum);
+} // DrawMenu
+
+int GPTDataCurses::MainMenu(void) {
+   if (((LINES - RESERVED_TOP - RESERVED_BOTTOM) < 2) || (COLS < 80)) {
+      Report("Display is too small; it must be at least 80 x 14 characters!");
+   } else {
+      if (GPTData::Verify() > 0)
+         Report("Warning! Problems found on disk! Use the Verify function to learn more.\n"
+                "Using gdisk or some other program may be necessary to repair the problems.");
+      IdentifySpaces();
+      currentSpaceNum = 0;
+      DrawMenu();
+      AcceptInput();
+   } // if/else
+   endwin();
+   return 0;
+} // GPTDataCurses::MainMenu
+
+/***********************************************************
+ *                                                         *
+ * Non-class support functions (mostly related to ncurses) *
+ *                                                         *
+ ***********************************************************/
+
+// Clears the specified line of all data....
+void ClearLine(int lineNum) {
+   move(lineNum, 0);
+   clrtoeol();
+} // ClearLine()
+
+// Clear the last few lines of the display
+void ClearBottom(void) {
+   move(LINES - RESERVED_BOTTOM, 0);
+   clrtobot();
+} // ClearBottom()
+
+void PromptToContinue(void) {
+   ClearBottom();
+   move(LINES - 2, (COLS - 29) / 2);
+   printw("Press any key to continue....");
+   cbreak();
+   getch();
+} // PromptToContinue()
+
+// Display one line of text on the screen and prompt to press any key to continue.
+void Report(string theText) {
+   clear();
+   move(0, 0);
+   printw(theText.c_str());
+   move(LINES - 2, (COLS - 29) / 2);
+   printw("Press any key to continue....");
+   cbreak();
+   getch();
+} // Report()
+
+// Displays all the partition type codes and then prompts to continue....
+// NOTE: This function temporarily exits curses mode as a matter of
+// convenience.
+void ShowTypes(void) {
+   PartType tempType;
+   char junk;
+
+   def_prog_mode();
+   endwin();
+   tempType.ShowAllTypes(LINES - 3);
+   cout << "\nPress the <Enter> key to continue: ";
+   cin.get(junk);
+   reset_prog_mode();
+   refresh();
+} // ShowTypes()
diff --git a/gptfdisk/gptcurses.h b/gptfdisk/gptcurses.h
new file mode 100644
index 0000000..9a14f7e
--- /dev/null
+++ b/gptfdisk/gptcurses.h
@@ -0,0 +1,136 @@
+/*
+ *    Implementation of GPTData class derivative with curses-based text-mode
+ *    interaction
+ *    Copyright (C) 2011-2013 Roderick W. Smith
+ *
+ *    This program is free software; you can redistribute it and/or modify
+ *    it under the terms of the GNU General Public License as published by
+ *    the Free Software Foundation; either version 2 of the License, or
+ *    (at your option) any later version.
+ *
+ *    This program is distributed in the hope that it will be useful,
+ *    but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *    GNU General Public License for more details.
+ *
+ *    You should have received a copy of the GNU General Public License along
+ *    with this program; if not, write to the Free Software Foundation, Inc.,
+ *    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <iostream>
+#include <string>
+#include "gptpart.h"
+#include "gpt.h"
+
+#ifndef __GPT_CURSES
+#define __GPT_CURSES
+
+using namespace std;
+
+struct MenuItem {
+   int key; // Keyboard shortcut
+   string name; // Item name; 8 characters
+   string desc; // Description
+};
+
+static struct MenuItem menuMain[] = {
+   { 'a', "Align ", "Set partition alignment policy" },
+   { 'b', "Backup", "Back up the partition table" },
+   { 'd', "Delete", "Delete the current partition" },
+   { 'h', " Help ", "Print help screen" },
+   { 'i', " Info ", "Display information about the partition" },
+   { 'l', " Load ", "Load partition table backup from file" },
+   { 'm', " naMe ", "Change the partition's name" },
+   { 'n', " New  ", "Create new partition from free space" },
+   { 'q', " Quit ", "Quit program without writing partition table" },
+   { 't', " Type ", "Change the filesystem type code GUID" },
+   { 'v', "Verify", "Verify the integrity of the disk's data structures" },
+   { 'w', "Write ", "Write partition table to disk (this might destroy data)" },
+   { 0, "", "" }
+};
+
+#define EMPTY_SPACE_OPTIONS "abhlnqvw"
+#define PARTITION_OPTIONS "abdhilmqtvw"
+
+// Constants for how to highlight a selected menu item
+#define USE_CURSES 1
+#define USE_ARROW 2
+
+// A "Space" is a partition or an unallocated chunk of disk space, maintained
+// in a doubly-linked-list data structure to facilitate creating displays of
+// partitions and unallocated chunks of space on the disk in the main
+// cgdisk partition list. This list MUST be correctly maintained and in order,
+// and the numSpaces variable in the main GPTDataCurses class must specify
+// how many Spaces are in the main linked list of Spaces.
+struct Space {
+   uint64_t firstLBA;
+   uint64_t lastLBA;
+   GPTPart *origPart;
+   int partNum;
+   Space *nextSpace;
+   Space *prevSpace;
+};
+
+class GPTDataCurses : public GPTData {
+protected:
+   static int numInstances;
+   GPTPart emptySpace;
+   Space *firstSpace;
+   Space *lastSpace;
+   Space *currentSpace;
+   int currentSpaceNum;
+   string whichOptions;
+   char currentKey;
+   int numSpaces;
+   int displayType;
+
+   // Functions relating to Spaces data structures
+   void EmptySpaces(void);
+   int MakeSpacesFromParts(void);
+   void AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA);
+   int AddEmptySpaces(void);
+   void UnlinkSpace(Space *theSpace);
+   void LinkToEnd(Space *theSpace);
+   void SortSpaces(void);
+   void IdentifySpaces(void);
+
+   // Data display functions
+   Space* ShowSpace(int spaceNum, int lineNum);
+   int DisplayParts(int selected);
+public:
+   GPTDataCurses(void);
+   ~GPTDataCurses(void);
+   // Functions corresponding to main menu items
+   void DeletePartition(int partNum);
+   void ShowInfo(int partNum);
+   void ChangeName(int partNum);
+   void ChangeType(int partNum);
+   void SetAlignment(void);
+   void Verify(void);
+   void MakeNewPart(void);
+   void SaveData(void);
+   void Backup(void);
+   void LoadBackup(void);
+   void ShowHelp(void);
+   // User input and menuing functions
+   void SetDisplayType(int dt) {displayType = dt;}
+   void ChangeSpaceSelection(int delta);
+   void MoveSelection(int delta);
+   void DisplayOptions(char selectedKey);
+   void AcceptInput();
+   int Dispatch(char operation);
+   void DrawMenu(void);
+   int MainMenu(void);
+}; // class GPTDataCurses
+
+// Non-class support functions (mostly to do simple curses stuff)....
+
+void ClearLine(int lineNum);
+void ClearBottom(void);
+void PromptToContinue(void);
+void Report(string theText);
+void ShowTypes(void);
+
+#endif
diff --git a/gptfdisk/gptpart.cc b/gptfdisk/gptpart.cc
new file mode 100644
index 0000000..7e89909
--- /dev/null
+++ b/gptfdisk/gptpart.cc
@@ -0,0 +1,456 @@
+//
+// C++ Implementation: gptpart
+//
+// Description: Class to implement a SINGLE GPT partition
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009-2013
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
+
+#define __STDC_LIMIT_MACROS
+#ifndef __STDC_CONSTANT_MACROS
+#define __STDC_CONSTANT_MACROS
+#endif
+
+#ifdef USE_UTF16
+#include <unicode/ustdio.h>
+#else
+#define UnicodeString string
+#endif
+
+#include <string.h>
+#include <stdio.h>
+#include <iostream>
+#include "gptpart.h"
+#include "attributes.h"
+
+using namespace std;
+
+GPTPart::GPTPart(void) {
+   partitionType.Zero();
+   uniqueGUID.Zero();
+   firstLBA = 0;
+   lastLBA = 0;
+   attributes = 0;
+   memset(name, 0, NAME_SIZE * sizeof(name[0]) );
+} // Default constructor
+
+GPTPart::~GPTPart(void) {
+} // destructor
+
+// Return the gdisk-specific two-byte hex code for the partition
+uint16_t GPTPart::GetHexType(void) const {
+   return partitionType.GetHexType();
+} // GPTPart::GetHexType()
+
+// Return a plain-text description of the partition type (e.g., "Linux/Windows
+// data" or "Linux swap").
+string GPTPart::GetTypeName(void) {
+   return partitionType.TypeName();
+} // GPTPart::GetNameType()
+
+#ifdef USE_UTF16
+// Return a Unicode description of the partition type (e.g., "Linux/Windows
+// data" or "Linux swap").
+UnicodeString GPTPart::GetUTypeName(void) {
+   return partitionType.UTypeName();
+} // GPTPart::GetNameType()
+#endif
+
+// Compute and return the partition's length (or 0 if the end is incorrectly
+// set before the beginning).
+uint64_t GPTPart::GetLengthLBA(void) const {
+   uint64_t length = 0;
+
+   if (firstLBA <= lastLBA)
+      length = lastLBA - firstLBA + UINT64_C(1);
+   return length;
+} // GPTPart::GetLengthLBA()
+
+#ifdef USE_UTF16
+// Return partition's name field, converted to a Unicode string
+UnicodeString GPTPart::GetDescription(void) {
+   return (UChar*) name;
+} // GPTPart::GetDescription()
+#else
+// Return partition's name field, converted to a C++ UTF-8 string
+string GPTPart::GetDescription(void) {
+   // convert name to utf32 then to utf8
+   string utf8 ;
+   size_t pos = 0 ;
+   while ( ( pos < NAME_SIZE ) && ( name[ pos ] != 0 ) ) {
+      uint16_t cp = name[ pos ++ ] ;
+      if ( ! IsLittleEndian() ) ReverseBytes( & cp , 2 ) ;
+      // first to utf32
+      uint32_t uni ;
+      if ( cp < 0xd800 || cp > 0xdfff ) {
+         uni = cp ;
+      } // if
+      else if ( cp < 0xdc00 ) {
+         // lead surrogate
+         uni = ( (uint32_t)( cp & 0x3ff ) ) << 10 ;
+         if ( pos >= NAME_SIZE ) {
+            // missing trail surrogate, name[] is invalid
+            break ;
+         } // if
+         cp = name[ pos ++ ] ;
+         if ( cp < 0xdc00 || cp > 0xdfff ) {
+            // invalid trail surrogate, name[] is invalid
+            break ;
+         } // if
+         // trail surrogate
+         uni |= cp & 0x3ff ;
+         uni += 0x10000 ;
+      } // if
+      else {
+         // unexpected trail surrogate, name[] is invalid
+         break ;
+      } // if
+      // then to utf8
+      if ( uni < 0x80 ) {
+         utf8 += (char) uni ;
+      } // if
+      else if ( uni < 0x800 ) {
+         utf8 += (char) ( 0xc0 | ( uni >> 6 ) ) ;
+         utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ;
+      } // if
+      else if ( uni < 0x10000 ) {
+         utf8 += (char) ( 0xe0 | ( uni >> 12 ) ) ;
+         utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ;
+         utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ;
+      } // if
+      else {
+         utf8 += (char) ( 0xf0 | ( uni >> 18 ) ) ;
+         utf8 += (char) ( 0xe0 | ( ( uni >> 12 ) & 0x3f ) ) ;
+         utf8 += (char) ( 0x80 | ( ( uni >> 6 ) & 0x3f ) ) ;
+         utf8 += (char) ( 0x80 | ( uni & 0x3f ) ) ;
+      } // if
+   }
+   return utf8 ;
+} // GPTPart::GetDescription(), UTF-8 version
+#endif
+
+// Return 1 if the partition is in use
+int GPTPart::IsUsed(void) {
+   return (partitionType != GUIDData("0x00"));
+} // GPTPart::IsUsed()
+
+// Returns MBR_SIZED_GOOD, MBR_SIZED_IFFY, or MBR_SIZED_BAD; see comments
+// in header file for details.
+int GPTPart::IsSizedForMBR(void) {
+   int retval = MBR_SIZED_GOOD;
+
+   if ((firstLBA > UINT32_MAX) || ((lastLBA - firstLBA) > UINT32_MAX) || (firstLBA > lastLBA))
+      retval = MBR_SIZED_BAD;
+   else if (lastLBA > UINT32_MAX)
+      retval = MBR_SIZED_IFFY;
+
+   return (retval);
+} // GPTPart::IsSizedForMBR()
+
+// Set the type code to the specified one. Also changes the partition
+// name *IF* the current name is the generic one for the current partition
+// type.
+void GPTPart::SetType(PartType t) {
+#ifdef USE_UTF16
+   if (GetDescription() == partitionType.UTypeName()) {
+#else
+   if (GetDescription() == partitionType.TypeName()) {
+#endif
+      SetName(t.TypeName());
+   } // if
+   partitionType = t;
+} // GPTPart::SetType()
+
+#ifdef USE_UTF16
+// Set the name for a partition to theName, using a C++-style string as
+// input.
+void GPTPart::SetName(const string & theName) {
+   SetName((UnicodeString) theName.c_str());
+} // GPTPart::SetName()
+
+// Set the name for a partition to theName, using a Unicode string as
+// input.
+void GPTPart::SetName(const UnicodeString & theName) {
+   if (theName.isBogus()) {
+      cerr << "Bogus UTF-16 name found in GPTPart::SetName()! Name not changed!\n";
+   } else {
+      memset(name, 0, NAME_SIZE * sizeof(name[0]) );
+      theName.extractBetween(0, NAME_SIZE, (UChar*) name);
+   } // if/else
+} // GPTPart::SetName()
+
+#else
+
+// Set the name for a partition to theName. Note that theName is a
+// standard C++-style ASCII 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) {
+   // convert utf8 to utf32 then to utf16le
+   size_t len = theName.length() ;
+   size_t pos = 0 ;
+   for ( size_t i = 0 ; pos < NAME_SIZE && i < len ; ) {
+      uint32_t uni ;
+      uint8_t cp = theName[ i ++ ] ;
+      int todo ;
+      if ( cp < 0x80 ) {
+         uni = cp ;
+         todo = 0 ;
+      } // if
+      else if ( cp < 0xc0 || cp > 0xf7 ) {
+         // invalid byte, theName is broken
+         break ;
+      } // if
+      else if ( cp < 0xe0 ) {
+         uni = cp & 0x1f ;
+         todo = 1 ;
+      } // if
+      else if ( cp < 0xf0 ) {
+         uni = cp & 0x0f ;
+         todo = 2 ;
+      } // if
+      else {
+         uni = cp & 0x7 ;
+         todo = 3 ;
+      } // if
+      while ( todo > 0 ) {
+         if ( i >= len ) {
+            // missing continuation byte, theName is broken
+            goto break_converter ;
+         } // if
+         cp = theName[ i ++ ] ;
+         if ( cp > 0xbf || cp < 0x80 ) {
+            // invalid continuation byte, theName is broken
+            goto break_converter ;
+         } // if
+         uni <<= 6 ;
+         uni |= cp & 0x3f ;
+         todo -- ;
+      } // while
+      // then to utf16le
+      if ( uni < 0x10000 ) {
+         name[ pos ] = (uint16_t) uni ;
+         if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ;
+         pos ++ ;
+      } // if
+      else {
+         if ( pos > NAME_SIZE - 2 ) {
+             // not enough room for two surrogates, truncate
+             break ;
+         } // if
+         uni -= 0x10000 ;
+         name[ pos ] = (uint16_t)( uni >> 10 ) | 0xd800 ;
+         if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ;
+         pos ++ ;
+         name[ pos ] = (uint16_t)( uni & 0x3ff ) | 0xdc00 ;
+         if ( ! IsLittleEndian() ) ReverseBytes( name + pos , 2 ) ;
+         pos ++ ;
+      }
+   } // for
+   break_converter : ;
+   // finally fill with zeroes
+   while ( pos < NAME_SIZE ) {
+      name[ pos ++ ] = 0 ;
+   } // while
+} // GPTPart::SetName(), UTF-8 version
+#endif
+
+// Set the name for the partition based on the current GUID partition type
+// code's associated name
+void GPTPart::SetDefaultDescription(void) {
+   SetName(partitionType.TypeName());
+} // GPTPart::SetDefaultDescription()
+
+GPTPart & GPTPart::operator=(const GPTPart & orig) {
+   partitionType = orig.partitionType;
+   uniqueGUID = orig.uniqueGUID;
+   firstLBA = orig.firstLBA;
+   lastLBA = orig.lastLBA;
+   attributes = orig.attributes;
+   memcpy(name, orig.name, NAME_SIZE * sizeof( name[ 0 ] ) );
+   return *this;
+} // assignment operator
+
+// Compare the values, and return a bool result.
+// Because this is intended for sorting and a firstLBA value of 0 denotes
+// a partition that's not in use and so that should be sorted upwards,
+// we return the opposite of the usual arithmetic result when either
+// firstLBA value is 0.
+bool GPTPart::operator<(const GPTPart &other) const {
+   if (firstLBA && other.firstLBA)
+      return (firstLBA < other.firstLBA);
+   else
+      return (other.firstLBA < firstLBA);
+} // GPTPart::operator<()
+
+// Display summary information; does nothing if the partition is empty.
+void GPTPart::ShowSummary(int partNum, uint32_t blockSize) {
+   string sizeInIeee;
+   UnicodeString description;
+   size_t i;
+
+   if (firstLBA != 0) {
+      sizeInIeee = BytesToIeee(lastLBA - firstLBA + 1, blockSize);
+      cout.fill(' ');
+      cout.width(4);
+      cout << partNum + 1 << "  ";
+      cout.width(14);
+      cout << firstLBA << "  ";
+      cout.width(14);
+      cout << lastLBA  << "   ";
+      cout << sizeInIeee << "  ";
+      if (sizeInIeee.length() < 10)
+         for (i = 0; i < 10 - sizeInIeee.length(); i++)
+            cout << " ";
+      cout.fill('0');
+      cout.width(4);
+      cout.setf(ios::uppercase);
+      cout << hex << partitionType.GetHexType() << "  " << dec;
+      cout.fill(' ');
+#ifdef USE_UTF16
+      GetDescription().extractBetween(0, 23, description);
+      cout << description << "\n";
+#else
+      string desc = GetDescription() ;
+      size_t n = 0 ;
+      size_t i = 0 ;
+      size_t len = desc.length() ;
+      while ( n < 22 && i < len ) {
+         i ++ ;
+         if ( i >= len ) {
+            // short description
+            break ;
+         } // if
+         // skip continuation bytes
+         while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) {
+             // utf8 continuation byte
+             i ++ ;
+         } // while
+         n ++ ;
+      } // while
+      if ( i < len ) {
+         n = 0 ;
+         i = 0 ;
+         // description is long we will truncate it
+         while ( n < 19 && i < len ) {
+            i ++ ;
+            if ( i >= len ) {
+               // should not happen
+               break ;
+            } // if
+            // skip continuation bytes
+            while ( i < len && ( ( desc[ i ] & 0xC0 ) == 0x80 ) ) {
+                // utf8 continuation byte
+                i ++ ;
+            } // while
+            n ++ ;
+         } // while
+      } // for
+      cout << GetDescription().substr( 0 , i ) ;
+      if ( i < len ) cout << "..." ;
+      cout << "\n";
+#endif
+      cout.fill(' ');
+   } // if
+} // GPTPart::ShowSummary()
+
+// Show detailed partition information. Does nothing if the partition is
+// empty (as determined by firstLBA being 0).
+void GPTPart::ShowDetails(uint32_t blockSize) {
+   uint64_t size;
+
+   if (firstLBA != 0) {
+      cout << "Partition GUID code: " << partitionType;
+      cout << " (" << partitionType.TypeName() << ")\n";
+      cout << "Partition unique GUID: " << uniqueGUID << "\n";
+
+      cout << "First sector: " << firstLBA << " (at "
+           << BytesToIeee(firstLBA, blockSize) << ")\n";
+      cout << "Last sector: " << lastLBA << " (at "
+           << BytesToIeee(lastLBA, blockSize) << ")\n";
+      size = (lastLBA - firstLBA + 1);
+      cout << "Partition size: " << size << " sectors ("
+           << BytesToIeee(size, blockSize) << ")\n";
+      cout << "Attribute flags: ";
+      cout.fill('0');
+      cout.width(16);
+      cout << hex;
+      cout << attributes << "\n";
+      cout << dec;
+      cout << "Partition name: '" << GetDescription() << "'\n";
+      cout.fill(' ');
+   }  // if
+} // GPTPart::ShowDetails()
+
+// Blank (delete) a single partition
+void GPTPart::BlankPartition(void) {
+   uniqueGUID.Zero();
+   partitionType.Zero();
+   firstLBA = 0;
+   lastLBA = 0;
+   attributes = 0;
+   memset(name, 0, NAME_SIZE * sizeof( name[0]) );
+} // GPTPart::BlankPartition
+
+// Returns 1 if the two partitions overlap, 0 if they don't
+int GPTPart::DoTheyOverlap(const GPTPart & other) {
+   // Don't bother checking unless these are defined (both start and end points
+   // are 0 for undefined partitions, so just check the start points)
+   return firstLBA && other.firstLBA &&
+          (firstLBA <= other.lastLBA) != (lastLBA < other.firstLBA);
+} // GPTPart::DoTheyOverlap()
+
+// Reverse the bytes of integral data types and of the UTF-16LE name;
+// used on big-endian systems.
+void GPTPart::ReversePartBytes(void) {
+   int i;
+
+   ReverseBytes(&firstLBA, 8);
+   ReverseBytes(&lastLBA, 8);
+   ReverseBytes(&attributes, 8);
+   for (i = 0; i < NAME_SIZE; i ++ )
+      ReverseBytes(name + i, 2);
+} // GPTPart::ReverseBytes()
+
+/****************************************
+ * Functions requiring user interaction *
+ ****************************************/
+
+// Change the type code on the partition. Also changes the name if the original
+// name is the generic one for the partition type.
+void GPTPart::ChangeType(void) {
+   string line;
+   int changeName;
+   PartType tempType = (GUIDData) "00000000-0000-0000-0000-000000000000";
+
+#ifdef USE_UTF16
+   changeName = (GetDescription() == GetUTypeName());
+#else
+   changeName = (GetDescription() == GetTypeName());
+#endif
+
+   cout << "Current type is '" << GetTypeName() << "'\n";
+   do {
+      cout << "Hex code or GUID (L to show codes, Enter = " << hex << DEFAULT_GPT_TYPE << dec << "): ";
+      line = ReadString();
+      if ((line[0] == 'L') || (line[0] == 'l')) {
+         partitionType.ShowAllTypes();
+      } else {
+         if (line.length() == 0)
+            tempType = DEFAULT_GPT_TYPE;
+         else
+            tempType = line;
+      } // if/else
+   } while (tempType == (GUIDData) "00000000-0000-0000-0000-000000000000");
+   partitionType = tempType;
+   cout << "Changed type of partition to '" << partitionType.TypeName() << "'\n";
+   if (changeName) {
+      SetDefaultDescription();
+   } // if
+} // GPTPart::ChangeType()
diff --git a/gptfdisk/gptpart.h b/gptfdisk/gptpart.h
new file mode 100644
index 0000000..657b3f9
--- /dev/null
+++ b/gptfdisk/gptpart.h
@@ -0,0 +1,101 @@
+//
+// C++ Interface: gptpart
+//
+// Description: Class to implement a single GPT partition
+//
+//
+// Author: Rod Smith <rodsmith@rodsbooks.com>, (C) 2009
+//
+// Copyright: See COPYING file that comes with this distribution
+//
+//
+// This program is copyright (c) 2009 by Roderick W. Smith. It is distributed
+// under the terms of the GNU GPL version 2, as detailed in the COPYING file.
+
+#ifndef __GPTPART_H
+#define __GPTPART_H
+
+#include <stdint.h>
+#include <string>
+#include <sys/types.h>
+#include "support.h"
+#include "parttypes.h"
+#include "guid.h"
+#include "attributes.h"
+
+using namespace std;
+
+// Values returned by GPTPart::IsSizedForMBR()
+#define MBR_SIZED_GOOD 0 /* Whole partition under 2^32 sectors */
+#define MBR_SIZED_IFFY 1 /* Partition starts under 2^32 & is less than 2^32, but ends over 2^32 */
+#define MBR_SIZED_BAD  2 /* Partition starts over 2^32, is bigger than 2^32, or otherwise bad */
+
+/****************************************
+ *                                      *
+ * GPTPart class and related structures *
+ *                                      *
+ ****************************************/
+
+class GPTPart {
+   protected:
+      // Caution: The non-static data in GPTPart is precisely the right size
+      // to enable easy loading of the data directly from disk. If any
+      // non-static variables are added to the below, the data size will
+      // change and the program will stop working. This can be corrected by
+      // adjusting the data-load operation in GPTData::LoadMainTable() and
+      // GPTData::LoadSecondTableAsMain() and then removing the GPTPart
+      // size check in SizesOK() (in gpt.cc file).
+      PartType partitionType;
+      GUIDData uniqueGUID;
+      uint64_t firstLBA;
+      uint64_t lastLBA;
+      Attributes attributes;
+      uint16_t name[NAME_SIZE];
+   public:
+      GPTPart(void);
+      ~GPTPart(void);
+
+      // Simple data retrieval:
+      PartType & GetType(void) {return partitionType;}
+      uint16_t GetHexType(void) const;
+      string GetTypeName(void);
+      UnicodeString GetUTypeName(void);
+      const GUIDData GetUniqueGUID(void) const {return uniqueGUID;}
+      uint64_t GetFirstLBA(void) const {return firstLBA;}
+      uint64_t GetLastLBA(void) const {return lastLBA;}
+      uint64_t GetLengthLBA(void) const;
+      Attributes GetAttributes(void) {return attributes;}
+      void ShowAttributes(uint32_t partNum) {attributes.ShowAttributes(partNum);}
+      UnicodeString GetDescription(void);
+      int IsUsed(void);
+      int IsSizedForMBR(void);
+
+      // Simple data assignment:
+      void SetType(PartType t);
+      void SetType(uint16_t hex) {partitionType = hex;}
+      void SetUniqueGUID(GUIDData u) {uniqueGUID = u;}
+      void RandomizeUniqueGUID(void) {uniqueGUID.Randomize();}
+      void SetFirstLBA(uint64_t f) {firstLBA = f;}
+      void SetLastLBA(uint64_t l) {lastLBA = l;}
+      void SetAttributes(uint64_t a) {attributes = a;}
+      void SetAttributes(void) {attributes.ChangeAttributes();}
+      void SetName(const string & theName);
+#ifdef USE_UTF16
+      void SetName(const UnicodeString & theName);
+#endif
+      void SetDefaultDescription(void);
+
+      // Additional functions
+      GPTPart & operator=(const GPTPart & orig);
+      bool operator<(const GPTPart &other) const;
+      void ShowSummary(int partNum, uint32_t blockSize); // display summary information (1-line)
+      void ShowDetails(uint32_t blockSize); // display detailed information (multi-line)
+      void BlankPartition(void); // empty partition of data
+      int DoTheyOverlap(const GPTPart & other); // returns 1 if there's overlap
+      void ReversePartBytes(void); // reverse byte order of all integer fields
+
+      // Functions requiring user interaction
+      void ChangeType(void); // Change the type code
+}; // struct GPTPart
+
+#endif
diff --git a/gptfdisk/gpttext.cc b/gptfdisk/gpttext.cc
new file mode 100644
index 0000000..718b99b
--- /dev/null
+++ b/gptfdisk/gpttext.cc
@@ -0,0 +1,945 @@
+/*
+    Copyright (C) 2010-2013  <Roderick W. Smith>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+*/
+
+/* This class implements an interactive text-mode interface atop the
+   GPTData class */
+
+#include <string.h>
+#include <errno.h>
+#include <stdint.h>
+#include <limits.h>
+#include <iostream>
+#include <fstream>
+#include <sstream>
+#include <cstdio>
+#include "attributes.h"
+#include "gpttext.h"
+#include "support.h"
+
+using namespace std;
+
+/********************************************
+ *                                          *
+ * GPTDataText class and related structures *
+ *                                          *
+ ********************************************/
+
+GPTDataTextUI::GPTDataTextUI(void) : GPTData() {
+} // default constructor
+
+GPTDataTextUI::GPTDataTextUI(string filename) : GPTData(filename) {
+} // constructor passing filename
+
+GPTDataTextUI::~GPTDataTextUI(void) {
+} // default destructor
+
+/*********************************************************************
+ *                                                                   *
+ * The following functions are extended (interactive) versions of    *
+ * simpler functions in the base class....                           *
+ *                                                                   *
+ *********************************************************************/
+
+// Overridden function; calls base-class function and then makes
+// additional queries of the user, if the base-class function can't
+// decide what to do.
+WhichToUse GPTDataTextUI::UseWhichPartitions(void) {
+   WhichToUse which;
+   MBRValidity mbrState;
+   int answer;
+
+   which = GPTData::UseWhichPartitions();
+   if ((which != use_abort) || beQuiet)
+      return which;
+
+   // If we get past here, it means that the non-interactive tests were
+   // inconclusive, so we must ask the user which table to use....
+   mbrState = protectiveMBR.GetValidity();
+
+   if ((state == gpt_valid) && (mbrState == mbr)) {
+      cout << "Found valid MBR and GPT. Which do you want to use?\n";
+      answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
+      if (answer == 1) {
+         which = use_mbr;
+      } else if (answer == 2) {
+         which = use_gpt;
+         cout << "Using GPT and creating fresh protective MBR.\n";
+      } else which = use_new;
+   } // if
+
+   // Nasty decisions here -- GPT is present, but corrupt (bad CRCs or other
+   // problems)
+   if (state == gpt_corrupt) {
+      if ((mbrState == mbr) || (mbrState == hybrid)) {
+         cout << "Found valid MBR and corrupt GPT. Which do you want to use? (Using the\n"
+              << "GPT MAY permit recovery of GPT data.)\n";
+         answer = GetNumber(1, 3, 2, " 1 - MBR\n 2 - GPT\n 3 - Create blank GPT\n\nYour answer: ");
+         if (answer == 1) {
+            which = use_mbr;
+         } else if (answer == 2) {
+            which = use_gpt;
+         } else which = use_new;
+      } else if (mbrState == invalid) {
+         cout << "Found invalid MBR and corrupt GPT. What do you want to do? (Using the\n"
+              << "GPT MAY permit recovery of GPT data.)\n";
+         answer = GetNumber(1, 2, 1, " 1 - Use current GPT\n 2 - Create blank GPT\n\nYour answer: ");
+         if (answer == 1) {
+            which = use_gpt;
+         } else which = use_new;
+      } // if/else/else
+   } // if (corrupt GPT)
+
+   return which;
+} // UseWhichPartitions()
+
+// Ask the user for a partition number; and prompt for verification
+// if the requested partition isn't of a known BSD type.
+// Lets the base-class function do the work, and returns its value (the
+// number of converted partitions).
+int GPTDataTextUI::XFormDisklabel(void) {
+   uint32_t partNum;
+   uint16_t hexCode;
+   int goOn = 1, numDone = 0;
+   BSDData disklabel;
+
+   partNum = GetPartNum();
+
+   // Now see if the specified partition has a BSD type code....
+   hexCode = partitions[partNum].GetHexType();
+   if ((hexCode != 0xa500) && (hexCode != 0xa900)) {
+      cout << "Specified partition doesn't have a disklabel partition type "
+           << "code.\nContinue anyway? ";
+      goOn = (GetYN() == 'Y');
+   } // if
+
+   if (goOn)
+      numDone = GPTData::XFormDisklabel(partNum);
+
+   return numDone;
+} // GPTData::XFormDisklabel(void)
+
+
+/*********************************************************************
+ *                                                                   *
+ * Begin functions that obtain information from the users, and often *
+ * do something with that information (call other functions)         *
+ *                                                                   *
+ *********************************************************************/
+
+// Prompts user for partition number and returns the result. Returns "0"
+// (the first partition) if none are currently defined.
+uint32_t GPTDataTextUI::GetPartNum(void) {
+   uint32_t partNum;
+   uint32_t low, high;
+   ostringstream prompt;
+
+   if (GetPartRange(&low, &high) > 0) {
+      prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): ";
+      partNum = GetNumber(low + 1, high + 1, low, prompt.str());
+   } else partNum = 1;
+   return (partNum - 1);
+} // GPTDataTextUI::GetPartNum()
+
+// What it says: Resize the partition table. (Default is 128 entries.)
+void GPTDataTextUI::ResizePartitionTable(void) {
+   int newSize;
+   ostringstream prompt;
+   uint32_t curLow, curHigh;
+
+   cout << "Current partition table size is " << numParts << ".\n";
+   GetPartRange(&curLow, &curHigh);
+   curHigh++; // since GetPartRange() returns numbers starting from 0...
+   // There's no point in having fewer than four partitions....
+   if (curHigh < (blockSize / GPT_SIZE))
+      curHigh = blockSize / GPT_SIZE;
+   prompt << "Enter new size (" << curHigh << " up, default " << NUM_GPT_ENTRIES << "): ";
+   newSize = GetNumber(4, 65535, 128, prompt.str());
+   if (newSize < 128) {
+      cout << "Caution: The partition table size should officially be 16KB or larger,\n"
+           << "which works out to 128 entries. In practice, smaller tables seem to\n"
+           << "work with most OSes, but this practice is risky. I'm proceeding with\n"
+           << "the resize, but you may want to reconsider this action and undo it.\n\n";
+   } // if
+   SetGPTSize(newSize);
+} // GPTDataTextUI::ResizePartitionTable()
+
+// Interactively create a partition
+void GPTDataTextUI::CreatePartition(void) {
+   uint64_t firstBlock, firstInLargest, lastBlock, sector, origSector;
+   uint32_t firstFreePart = 0;
+   ostringstream prompt1, prompt2, prompt3;
+   int partNum;
+
+   // Find first free partition...
+   while (partitions[firstFreePart].GetFirstLBA() != 0) {
+      firstFreePart++;
+   } // while
+
+   if (((firstBlock = FindFirstAvailable()) != 0) &&
+       (firstFreePart < numParts)) {
+      lastBlock = FindLastAvailable();
+      firstInLargest = FindFirstInLargest();
+      Align(&firstInLargest);
+
+      // Get partition number....
+      prompt1 << "Partition number (" << firstFreePart + 1 << "-" << numParts
+              << ", default " << firstFreePart + 1 << "): ";
+      do {
+         partNum = GetNumber(firstFreePart + 1, 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{KMGTP}: ";
+      do {
+         sector = GetSectorNum(firstBlock, lastBlock, firstInLargest, blockSize, prompt2.str());
+      } while (IsFree(sector) == 0);
+      origSector = sector;
+      if (Align(&sector)) {
+         cout << "Information: Moved requested sector from " << origSector << " to "
+              << sector << " in\norder to align on " << sectorAlignment
+              << "-sector boundaries.\n";
+         if (!beQuiet)
+            cout << "Use 'l' on the experts' menu to adjust alignment\n";
+      } // if
+      //      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{KMGTP}: ";
+      do {
+         sector = GetSectorNum(firstBlock, lastBlock, lastBlock, blockSize, prompt3.str());
+      } while (IsFree(sector) == 0);
+      lastBlock = sector;
+
+      firstFreePart = GPTData::CreatePartition(partNum, firstBlock, lastBlock);
+      partitions[partNum].ChangeType();
+      partitions[partNum].SetDefaultDescription();
+   } else {
+      if (firstFreePart >= numParts)
+         cout << "No table partition entries left\n";
+      else
+         cout << "No free sectors available\n";
+   } // if/else
+} // GPTDataTextUI::CreatePartition()
+
+// Interactively delete a partition (duh!)
+void GPTDataTextUI::DeletePartition(void) {
+   int partNum;
+   uint32_t low, high;
+   ostringstream prompt;
+
+   if (GetPartRange(&low, &high) > 0) {
+      prompt << "Partition number (" << low + 1 << "-" << high + 1 << "): ";
+      partNum = GetNumber(low + 1, high + 1, low, prompt.str());
+      GPTData::DeletePartition(partNum - 1);
+   } else {
+      cout << "No partitions\n";
+   } // if/else
+} // GPTDataTextUI::DeletePartition()
+
+// Prompt user for a partition number, then change its type code
+void GPTDataTextUI::ChangePartType(void) {
+   int partNum;
+   uint32_t low, high;
+
+   if (GetPartRange(&low, &high) > 0) {
+      partNum = GetPartNum();
+      partitions[partNum].ChangeType();
+   } else {
+      cout << "No partitions\n";
+   } // if/else
+} // GPTDataTextUI::ChangePartType()
+
+// Prompt user for a partition number, then change its unique
+// GUID.
+void GPTDataTextUI::ChangeUniqueGuid(void) {
+   int partNum;
+   uint32_t low, high;
+   string guidStr;
+
+   if (GetPartRange(&low, &high) > 0) {
+      partNum = GetPartNum();
+      cout << "Enter the partition's new unique GUID ('R' to randomize): ";
+      guidStr = ReadString();
+      if ((guidStr.length() >= 32) || (guidStr[0] == 'R') || (guidStr[0] == 'r')) {
+         SetPartitionGUID(partNum, (GUIDData) guidStr);
+         cout << "New GUID is " << partitions[partNum].GetUniqueGUID() << "\n";
+      } else {
+         cout << "GUID is too short!\n";
+      } // if/else
+   } else
+      cout << "No partitions\n";
+} // GPTDataTextUI::ChangeUniqueGuid()
+
+// Partition attributes seem to be rarely used, but I want a way to
+// adjust them for completeness....
+void GPTDataTextUI::SetAttributes(uint32_t partNum) {
+   partitions[partNum].SetAttributes();
+} // GPTDataTextUI::SetAttributes()
+
+// Prompts the user for a partition name and sets the partition's
+// name. Returns 1 on success, 0 on failure (invalid partition
+// number). (Note that the function skips prompting when an
+// invalid partition number is detected.)
+int GPTDataTextUI::SetName(uint32_t partNum) {
+   UnicodeString theName = "";
+   int retval = 1;
+
+   if (IsUsedPartNum(partNum)) {
+      cout << "Enter name: ";
+#ifdef USE_UTF16
+      theName = ReadUString();
+#else
+      theName = ReadString();
+#endif
+      partitions[partNum].SetName(theName);
+   } else {
+      cerr << "Invalid partition number (" << partNum << ")\n";
+      retval = 0;
+   } // if/else
+
+   return retval;
+} // GPTDataTextUI::SetName()
+
+// Ask user for two partition numbers and swap them in the table. Note that
+// this just reorders table entries; it doesn't adjust partition layout on
+// the disk.
+// Returns 1 if successful, 0 if not. (If user enters identical numbers, it
+// counts as successful.)
+int GPTDataTextUI::SwapPartitions(void) {
+   int partNum1, partNum2, didIt = 0;
+   uint32_t low, high;
+   ostringstream prompt;
+   GPTPart temp;
+
+   if (GetPartRange(&low, &high) > 0) {
+      partNum1 = GetPartNum();
+      if (high >= numParts - 1)
+         high = 0;
+      prompt << "New partition number (1-" << numParts
+             << ", default " << high + 2 << "): ";
+      partNum2 = GetNumber(1, numParts, high + 2, prompt.str()) - 1;
+      didIt = GPTData::SwapPartitions(partNum1, partNum2);
+   } else {
+      cout << "No partitions\n";
+   } // if/else
+   return didIt;
+} // GPTDataTextUI::SwapPartitionNumbers()
+
+// This function destroys the on-disk GPT structures. Returns 1 if the user
+// confirms destruction, 0 if the user aborts or if there's a disk error.
+int GPTDataTextUI::DestroyGPTwPrompt(void) {
+   int allOK = 1;
+
+   if ((apmFound) || (bsdFound)) {
+      cout << "WARNING: APM or BSD disklabel structures detected! This operation could\n"
+           << "damage any APM or BSD partitions on this disk!\n";
+   } // if APM or BSD
+   cout << "\a\aAbout to wipe out GPT on " << device << ". Proceed? ";
+   if (GetYN() == 'Y') {
+      if (DestroyGPT()) {
+         // Note on below: Touch the MBR only if the user wants it completely
+         // blanked out. Version 0.4.2 deleted the 0xEE partition and re-wrote
+         // the MBR, but this could wipe out a valid MBR that the program
+         // had subsequently discarded (say, if it conflicted with older GPT
+         // structures).
+         cout << "Blank out MBR? ";
+         if (GetYN() == 'Y') {
+            DestroyMBR();
+         } else {
+            cout << "MBR is unchanged. You may need to delete an EFI GPT (0xEE) partition\n"
+                 << "with fdisk or another tool.\n";
+         } // if/else
+      } else allOK = 0; // if GPT structures destroyed
+   } else allOK = 0; // if user confirms destruction
+   return (allOK);
+} // GPTDataTextUI::DestroyGPTwPrompt()
+
+// Get partition number from user and then call ShowPartDetails(partNum)
+// to show its detailed information
+void GPTDataTextUI::ShowDetails(void) {
+   int partNum;
+   uint32_t low, high;
+
+   if (GetPartRange(&low, &high) > 0) {
+      partNum = GetPartNum();
+      ShowPartDetails(partNum);
+   } else {
+      cout << "No partitions\n";
+   } // if/else
+} // GPTDataTextUI::ShowDetails()
+
+// Create a hybrid MBR -- an ugly, funky thing that helps GPT work with
+// OSes that don't understand GPT.
+void GPTDataTextUI::MakeHybrid(void) {
+   uint32_t partNums[3] = {0, 0, 0};
+   string line;
+   int numPartsToCvt = 0, numConverted = 0, i, j, mbrNum = 0;
+   unsigned int hexCode = 0;
+   MBRPart hybridPart;
+   MBRData hybridMBR;
+   char eeFirst = 'Y'; // Whether EFI GPT (0xEE) partition comes first in table
+
+   cout << "\nWARNING! Hybrid MBRs are flaky and dangerous! If you decide not to use one,\n"
+        << "just hit the Enter key at the below prompt and your MBR partition table will\n"
+        << "be untouched.\n\n\a";
+
+   // Use a local MBR structure, copying from protectiveMBR to keep its
+   // boot loader code intact....
+   hybridMBR = protectiveMBR;
+   hybridMBR.EmptyMBR(0);
+
+   // Now get the numbers of up to three partitions to add to the
+   // hybrid MBR....
+   cout << "Type from one to three GPT partition numbers, separated by spaces, to be\n"
+        << "added to the hybrid MBR, in sequence: ";
+   line = ReadString();
+   istringstream inLine(line);
+   do {
+      inLine >> partNums[numPartsToCvt];
+      if (partNums[numPartsToCvt] > 0)
+         numPartsToCvt++;
+   } while (!inLine.eof() && (numPartsToCvt < 3));
+
+   if (numPartsToCvt > 0) {
+      cout << "Place EFI GPT (0xEE) partition first in MBR (good for GRUB)? ";
+      eeFirst = GetYN();
+   } // if
+
+   for (i = 0; i < numPartsToCvt; i++) {
+      j = partNums[i] - 1;
+      if (partitions[j].IsUsed() && (partitions[j].IsSizedForMBR() != MBR_SIZED_BAD)) {
+         mbrNum = i + (eeFirst == 'Y');
+         cout << "\nCreating entry for GPT partition #" << j + 1
+              << " (MBR partition #" << mbrNum + 1 << ")\n";
+         hybridPart.SetType(GetMBRTypeCode(partitions[j].GetHexType() / 256));
+         hybridPart.SetLocation(partitions[j].GetFirstLBA(), partitions[j].GetLengthLBA());
+         hybridPart.SetInclusion(PRIMARY);
+         cout << "Set the bootable flag? ";
+         if (GetYN() == 'Y')
+            hybridPart.SetStatus(0x80);
+         else
+            hybridPart.SetStatus(0x00);
+         hybridPart.SetInclusion(PRIMARY);
+         if (partitions[j].IsSizedForMBR() == MBR_SIZED_IFFY)
+            WarnAboutIffyMBRPart(j + 1);
+         numConverted++;
+      } else {
+         cerr << "\nGPT partition #" << j + 1 << " does not exist or is too big; skipping.\n";
+      } // if/else
+      hybridMBR.AddPart(mbrNum, hybridPart);
+   } // for
+
+   if (numConverted > 0) { // User opted to create a hybrid MBR....
+      // Create EFI protective partition that covers the start of the disk.
+      // If this location (covering the main GPT data structures) is omitted,
+      // Linux won't find any partitions on the disk.
+      hybridPart.SetLocation(1, hybridMBR.FindLastInFree(1));
+      hybridPart.SetStatus(0);
+      hybridPart.SetType(0xEE);
+      hybridPart.SetInclusion(PRIMARY);
+      // newNote firstLBA and lastLBA are computed later...
+      if (eeFirst == 'Y') {
+         hybridMBR.AddPart(0, hybridPart);
+      } else {
+         hybridMBR.AddPart(numConverted, hybridPart);
+      } // else
+      hybridMBR.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....
+      if (hybridMBR.CountParts() < 4) { // unused entry....
+         cout << "\nUnused partition space(s) found. Use one to protect more partitions? ";
+         if (GetYN() == 'Y') {
+            cout << "Note: Default is 0xEE, but this may confuse Mac OS X.\n";
+            // Comment on above: Mac OS treats disks with more than one
+            // 0xEE MBR partition as MBR disks, not as GPT disks.
+            hexCode = GetMBRTypeCode(0xEE);
+            hybridMBR.MakeBiggestPart(3, hexCode);
+         } // if (GetYN() == 'Y')
+      } // if unused entry
+      protectiveMBR = hybridMBR;
+   } else {
+      cout << "\nNo partitions converted; original protective/hybrid MBR is unmodified!\n";
+   } // if/else (numConverted > 0)
+} // GPTDataTextUI::MakeHybrid()
+
+// 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, call SaveMBR() to save the MBR, and then exit.
+int GPTDataTextUI::XFormToMBR(void) {
+   uint32_t i;
+
+   protectiveMBR.EmptyMBR(0);
+   for (i = 0; i < numParts; i++) {
+      if (partitions[i].IsUsed()) {
+         if (partitions[i].IsSizedForMBR() == MBR_SIZED_IFFY)
+            WarnAboutIffyMBRPart(i + 1);
+         // Note: MakePart() checks for oversized partitions, so don't
+         // bother checking other IsSizedForMBR() return values....
+         protectiveMBR.MakePart(i, partitions[i].GetFirstLBA(),
+                                partitions[i].GetLengthLBA(),
+                                partitions[i].GetHexType() / 0x0100, 0);
+      } // if
+   } // for
+   protectiveMBR.MakeItLegal();
+   return protectiveMBR.DoMenu();
+} // GPTDataTextUI::XFormToMBR()
+
+/******************************************************
+ *                                                    *
+ * Display informational messages for the user....    *
+ *                                                    *
+ ******************************************************/
+
+// Although an MBR partition that begins below sector 2^32 and is less than 2^32 sectors in
+// length is technically legal even if it ends above the 2^32-sector mark, such a partition
+// tends to confuse a lot of OSes, so warn the user about such partitions. This function is
+// called by XFormToMBR() and MakeHybrid(); it's a separate function just to consolidate the
+// lengthy message in one place.
+void GPTDataTextUI::WarnAboutIffyMBRPart(int partNum) {
+   cout << "\a\nWarning! GPT partition " << partNum << " ends after the 2^32 sector mark! The partition\n"
+        << "begins before this point, and is smaller than 2^32 sectors. This is technically\n"
+        << "legal, but will confuse some OSes. The partition IS being added to the MBR, but\n"
+        << "if your OS misbehaves or can't see the partition, the partition may simply be\n"
+        << "unusable in that OS and may need to be resized or omitted from the MBR.\n\n";
+} // GPTDataTextUI::WarnAboutIffyMBRPart()
+
+/*********************************************************************
+ *                                                                   *
+ * The following functions provide the main menus for the gdisk      *
+ * program....                                                       *
+ *                                                                   *
+ *********************************************************************/
+
+// Accept a command and execute it. Returns only when the user
+// wants to exit (such as after a 'w' or 'q' command).
+void GPTDataTextUI::MainMenu(string filename) {
+   int goOn = 1;
+   PartType typeHelper;
+   uint32_t temp1, temp2;
+
+   do {
+      cout << "\nCommand (? for help): ";
+      switch (ReadString()[0]) {
+         case '\0':
+            goOn = cin.good();
+            break;
+         case 'b': case 'B':
+            cout << "Enter backup filename to save: ";
+            SaveGPTBackup(ReadString());
+            break;
+         case 'c': case 'C':
+            if (GetPartRange(&temp1, &temp2) > 0)
+               SetName(GetPartNum());
+            else
+               cout << "No partitions\n";
+            break;
+         case 'd': case 'D':
+            DeletePartition();
+            break;
+         case 'i': case 'I':
+            ShowDetails();
+            break;
+         case 'l': case 'L':
+            typeHelper.ShowAllTypes();
+            break;
+         case 'n': case 'N':
+            CreatePartition();
+            break;
+         case 'o': case 'O':
+            cout << "This option deletes all partitions and creates a new protective MBR.\n"
+                 << "Proceed? ";
+            if (GetYN() == 'Y') {
+               ClearGPTData();
+               MakeProtectiveMBR();
+            } // if
+            break;
+         case 'p': case 'P':
+            DisplayGPTData();
+            break;
+         case 'q': case 'Q':
+            goOn = 0;
+            break;
+         case 'r': case 'R':
+            RecoveryMenu(filename);
+            goOn = 0;
+            break;
+         case 's': case 'S':
+            SortGPT();
+            cout << "You may need to edit /etc/fstab and/or your boot loader configuration!\n";
+            break;
+         case 't': case 'T':
+            ChangePartType();
+            break;
+         case 'v': case 'V':
+            Verify();
+            break;
+         case 'w': case 'W':
+            if (SaveGPTData() == 1)
+               goOn = 0;
+            break;
+         case 'x': case 'X':
+            ExpertsMenu(filename);
+            goOn = 0;
+            break;
+         default:
+            ShowCommands();
+            break;
+      } // switch
+   } while (goOn);
+} // GPTDataTextUI::MainMenu()
+
+void GPTDataTextUI::ShowCommands(void) {
+   cout << "b\tback up GPT data to a file\n";
+   cout << "c\tchange a partition's name\n";
+   cout << "d\tdelete a partition\n";
+   cout << "i\tshow detailed information on a partition\n";
+   cout << "l\tlist known partition types\n";
+   cout << "n\tadd a new partition\n";
+   cout << "o\tcreate a new empty GUID partition table (GPT)\n";
+   cout << "p\tprint the partition table\n";
+   cout << "q\tquit without saving changes\n";
+   cout << "r\trecovery and transformation options (experts only)\n";
+   cout << "s\tsort partitions\n";
+   cout << "t\tchange a partition's type code\n";
+   cout << "v\tverify disk\n";
+   cout << "w\twrite table to disk and exit\n";
+   cout << "x\textra functionality (experts only)\n";
+   cout << "?\tprint this menu\n";
+} // GPTDataTextUI::ShowCommands()
+
+// Accept a recovery & transformation menu command. Returns only when the user
+// issues an exit command, such as 'w' or 'q'.
+void GPTDataTextUI::RecoveryMenu(string filename) {
+   uint32_t numParts;
+   int goOn = 1, temp1;
+   
+   do {
+      cout << "\nRecovery/transformation command (? for help): ";
+      switch (ReadString()[0]) {
+         case '\0':
+            goOn = cin.good();
+            break;
+         case 'b': case 'B':
+            RebuildMainHeader();
+            break;
+         case 'c': case 'C':
+            cout << "Warning! This will probably do weird things if you've converted an MBR to\n"
+            << "GPT form and haven't yet saved the GPT! Proceed? ";
+            if (GetYN() == 'Y')
+               LoadSecondTableAsMain();
+            break;
+         case 'd': case 'D':
+            RebuildSecondHeader();
+            break;
+         case 'e': case 'E':
+            cout << "Warning! This will probably do weird things if you've converted an MBR to\n"
+            << "GPT form and haven't yet saved the GPT! Proceed? ";
+            if (GetYN() == 'Y')
+               LoadMainTable();
+            break;
+         case 'f': case 'F':
+            cout << "Warning! This will destroy the currently defined partitions! Proceed? ";
+            if (GetYN() == 'Y') {
+               if (LoadMBR(filename) == 1) { // successful load
+                  XFormPartitions();
+               } else {
+                  cout << "Problem loading MBR! GPT is untouched; regenerating protective MBR!\n";
+                  MakeProtectiveMBR();
+               } // if/else
+            } // if
+            break;
+         case 'g': case 'G':
+            numParts = GetNumParts();
+            temp1 = XFormToMBR();
+            if (temp1 > 0)
+               cout << "\nConverted " << temp1 << " partitions. Finalize and exit? ";
+            if ((temp1 > 0) && (GetYN() == 'Y')) {
+               if ((DestroyGPT() > 0) && (SaveMBR())) {
+                  goOn = 0;
+               } // if
+            } else {
+               MakeProtectiveMBR();
+               SetGPTSize(numParts, 0);
+               cout << "Note: New protective MBR created\n\n";
+            } // if/else
+            break;
+         case 'h': case 'H':
+            MakeHybrid();
+            break;
+         case 'i': case 'I':
+            ShowDetails();
+            break;
+         case 'l': case 'L':
+            cout << "Enter backup filename to load: ";
+            LoadGPTBackup(ReadString());
+            break;
+         case 'm': case 'M':
+            MainMenu(filename);
+            goOn = 0;
+            break;
+         case 'o': case 'O':
+            DisplayMBRData();
+            break;
+         case 'p': case 'P':
+            DisplayGPTData();
+            break;
+         case 'q': case 'Q':
+            goOn = 0;
+            break;
+         case 't': case 'T':
+            XFormDisklabel();
+            break;
+         case 'v': case 'V':
+            Verify();
+            break;
+         case 'w': case 'W':
+            if (SaveGPTData() == 1) {
+               goOn = 0;
+            } // if
+            break;
+         case 'x': case 'X':
+            ExpertsMenu(filename);
+            goOn = 0;
+            break;
+         default:
+            ShowRecoveryCommands();
+            break;
+      } // switch
+   } while (goOn);
+} // GPTDataTextUI::RecoveryMenu()
+
+void GPTDataTextUI::ShowRecoveryCommands(void) {
+   cout << "b\tuse backup GPT header (rebuilding main)\n";
+   cout << "c\tload backup partition table from disk (rebuilding main)\n";
+   cout << "d\tuse main GPT header (rebuilding backup)\n";
+   cout << "e\tload main partition table from disk (rebuilding backup)\n";
+   cout << "f\tload MBR and build fresh GPT from it\n";
+   cout << "g\tconvert GPT into MBR and exit\n";
+   cout << "h\tmake hybrid MBR\n";
+   cout << "i\tshow detailed information on a partition\n";
+   cout << "l\tload partition data from a backup file\n";
+   cout << "m\treturn to main menu\n";
+   cout << "o\tprint protective MBR data\n";
+   cout << "p\tprint the partition table\n";
+   cout << "q\tquit without saving changes\n";
+   cout << "t\ttransform BSD disklabel partition\n";
+   cout << "v\tverify disk\n";
+   cout << "w\twrite table to disk and exit\n";
+   cout << "x\textra functionality (experts only)\n";
+   cout << "?\tprint this menu\n";
+} // GPTDataTextUI::ShowRecoveryCommands()
+
+// Accept an experts' menu command. Returns only after the user
+// selects an exit command, such as 'w' or 'q'.
+void GPTDataTextUI::ExpertsMenu(string filename) {
+   GPTData secondDevice;
+   uint32_t temp1, temp2;
+   int goOn = 1;
+   string guidStr, device;
+   GUIDData aGUID;
+   ostringstream prompt;
+   
+   do {
+      cout << "\nExpert command (? for help): ";
+      switch (ReadString()[0]) {
+         case '\0':
+            goOn = cin.good();
+            break;
+         case 'a': case 'A':
+            if (GetPartRange(&temp1, &temp2) > 0)
+               SetAttributes(GetPartNum());
+            else
+               cout << "No partitions\n";
+            break;
+         case 'c': case 'C':
+            ChangeUniqueGuid();
+            break;
+         case 'd': case 'D':
+            cout << "Partitions will begin on " << GetAlignment()
+            << "-sector boundaries.\n";
+            break;
+         case 'e': case 'E':
+            cout << "Relocating backup data structures to the end of the disk\n";
+            MoveSecondHeaderToEnd();
+            break;
+         case 'f': case 'F':
+            RandomizeGUIDs();
+            break;
+         case 'g': case 'G':
+            cout << "Enter the disk's unique GUID ('R' to randomize): ";
+            guidStr = ReadSt