You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
824 lines
26 KiB
824 lines
26 KiB
/*
|
|
* Implementation of GPTData class derivative with curses-based text-mode
|
|
* interaction
|
|
* Copyright (C) 2011-2018 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 <clocale>
|
|
#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;
|
|
char conversion_specifier[] = "%d";
|
|
|
|
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(conversion_specifier, &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()
|