1/* 2 * Implementation of GPTData class derivative with curses-based text-mode 3 * interaction 4 * Copyright (C) 2011-2013 Roderick W. Smith 5 * 6 * This program is free software; you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License as published by 8 * the Free Software Foundation; either version 2 of the License, or 9 * (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License along 17 * with this program; if not, write to the Free Software Foundation, Inc., 18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19 * 20 */ 21 22#include <iostream> 23#include <string> 24#include <sstream> 25#include <ncurses.h> 26#include "gptcurses.h" 27#include "support.h" 28 29using namespace std; 30 31// # of lines to reserve for general information and headers (RESERVED_TOP) 32// and for options and messages (RESERVED_BOTTOM) 33#define RESERVED_TOP 7 34#define RESERVED_BOTTOM 5 35 36int GPTDataCurses::numInstances = 0; 37 38GPTDataCurses::GPTDataCurses(void) { 39 if (numInstances > 0) { 40 refresh(); 41 } else { 42 setlocale( LC_ALL , "" ); 43 initscr(); 44 cbreak(); 45 noecho(); 46 intrflush(stdscr, false); 47 keypad(stdscr, true); 48 nonl(); 49 numInstances++; 50 } // if/else 51 firstSpace = NULL; 52 lastSpace = NULL; 53 currentSpace = NULL; 54 currentSpaceNum = -1; 55 whichOptions = ""; // current set of options 56 currentKey = 'b'; // currently selected option 57 displayType = USE_CURSES; 58} // GPTDataCurses constructor 59 60GPTDataCurses::~GPTDataCurses(void) { 61 numInstances--; 62 if ((numInstances == 0) && !isendwin()) 63 endwin(); 64} // GPTDataCurses destructor 65 66/************************************************ 67 * * 68 * Functions relating to Spaces data structures * 69 * * 70 ************************************************/ 71 72void GPTDataCurses::EmptySpaces(void) { 73 Space *trash; 74 75 while (firstSpace != NULL) { 76 trash = firstSpace; 77 firstSpace = firstSpace->nextSpace; 78 delete trash; 79 } // if 80 numSpaces = 0; 81 lastSpace = NULL; 82} // GPTDataCurses::EmptySpaces() 83 84// Create Spaces from partitions. Does NOT creates Spaces to represent 85// unpartitioned space on the disk. 86// Returns the number of Spaces created. 87int GPTDataCurses::MakeSpacesFromParts(void) { 88 uint i; 89 Space *tempSpace; 90 91 EmptySpaces(); 92 for (i = 0; i < numParts; i++) { 93 if (partitions[i].IsUsed()) { 94 tempSpace = new Space; 95 tempSpace->firstLBA = partitions[i].GetFirstLBA(); 96 tempSpace->lastLBA = partitions[i].GetLastLBA(); 97 tempSpace->origPart = &partitions[i]; 98 tempSpace->partNum = (int) i; 99 LinkToEnd(tempSpace); 100 } // if 101 } // for 102 return numSpaces; 103} // GPTDataCurses::MakeSpacesFromParts() 104 105// Add a single empty Space to the current Spaces linked list and sort the result.... 106void GPTDataCurses::AddEmptySpace(uint64_t firstLBA, uint64_t lastLBA) { 107 Space *tempSpace; 108 109 tempSpace = new Space; 110 tempSpace->firstLBA = firstLBA; 111 tempSpace->lastLBA = lastLBA; 112 tempSpace->origPart = &emptySpace; 113 tempSpace->partNum = -1; 114 LinkToEnd(tempSpace); 115 SortSpaces(); 116} // GPTDataCurses::AddEmptySpace(); 117 118// Add Spaces to represent the unallocated parts of the partition table. 119// Returns the number of Spaces added. 120int GPTDataCurses::AddEmptySpaces(void) { 121 int numAdded = 0; 122 Space *current; 123 124 SortSpaces(); 125 if (firstSpace == NULL) { 126 AddEmptySpace(GetFirstUsableLBA(), GetLastUsableLBA()); 127 numAdded++; 128 } else { 129 current = firstSpace; 130 while ((current != NULL) /* && (current->partNum != -1) */ ) { 131 if ((current == firstSpace) && (current->firstLBA > GetFirstUsableLBA())) { 132 AddEmptySpace(GetFirstUsableLBA(), current->firstLBA - 1); 133 numAdded++; 134 } // if 135 if ((current == lastSpace) && (current->lastLBA < GetLastUsableLBA())) { 136 AddEmptySpace(current->lastLBA + 1, GetLastUsableLBA()); 137 numAdded++; 138 } // if 139 if ((current->prevSpace != NULL) && (current->prevSpace->lastLBA < (current->firstLBA - 1))) { 140 AddEmptySpace(current->prevSpace->lastLBA + 1, current->firstLBA - 1); 141 numAdded++; 142 } // if 143 current = current->nextSpace; 144 } // while 145 } // if/else 146 return numAdded; 147} // GPTDataCurses::AddEmptySpaces() 148 149// Remove the specified Space from the linked list and set its previous and 150// next pointers to NULL. 151void GPTDataCurses::UnlinkSpace(Space *theSpace) { 152 if (theSpace != NULL) { 153 if (theSpace->prevSpace != NULL) 154 theSpace->prevSpace->nextSpace = theSpace->nextSpace; 155 if (theSpace->nextSpace != NULL) 156 theSpace->nextSpace->prevSpace = theSpace->prevSpace; 157 if (theSpace == firstSpace) 158 firstSpace = theSpace->nextSpace; 159 if (theSpace == lastSpace) 160 lastSpace = theSpace->prevSpace; 161 theSpace->nextSpace = NULL; 162 theSpace->prevSpace = NULL; 163 numSpaces--; 164 } // if 165} // GPTDataCurses::UnlinkSpace 166 167// Link theSpace to the end of the current linked list. 168void GPTDataCurses::LinkToEnd(Space *theSpace) { 169 if (lastSpace == NULL) { 170 firstSpace = lastSpace = theSpace; 171 theSpace->nextSpace = NULL; 172 theSpace->prevSpace = NULL; 173 } else { 174 theSpace->prevSpace = lastSpace; 175 theSpace->nextSpace = NULL; 176 lastSpace->nextSpace = theSpace; 177 lastSpace = theSpace; 178 } // if/else 179 numSpaces++; 180} // GPTDataCurses::LinkToEnd() 181 182// Sort spaces into ascending order by on-disk position. 183void GPTDataCurses::SortSpaces(void) { 184 Space *oldFirst, *oldLast, *earliest = NULL, *current = NULL; 185 186 oldFirst = firstSpace; 187 oldLast = lastSpace; 188 firstSpace = lastSpace = NULL; 189 while (oldFirst != NULL) { 190 current = earliest = oldFirst; 191 while (current != NULL) { 192 if (current->firstLBA < earliest->firstLBA) 193 earliest = current; 194 current = current->nextSpace; 195 } // while 196 if (oldFirst == earliest) 197 oldFirst = earliest->nextSpace; 198 if (oldLast == earliest) 199 oldLast = earliest->prevSpace; 200 UnlinkSpace(earliest); 201 LinkToEnd(earliest); 202 } // while 203} // GPTDataCurses::SortSpaces() 204 205// Identify the spaces on the disk, a "space" being defined as a partition 206// or an empty gap between, before, or after partitions. The spaces are 207// presented to users in the main menu display. 208void GPTDataCurses::IdentifySpaces(void) { 209 MakeSpacesFromParts(); 210 AddEmptySpaces(); 211} // GPTDataCurses::IdentifySpaces() 212 213/************************** 214 * * 215 * Data display functions * 216 * * 217 **************************/ 218 219// Display a single Space on line # lineNum. 220// Returns a pointer to the space being displayed 221Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) { 222 Space *space; 223 int i = 0; 224#ifdef USE_UTF16 225 char temp[40]; 226#endif 227 228 space = firstSpace; 229 while ((space != NULL) && (i < spaceNum)) { 230 space = space->nextSpace; 231 i++; 232 } // while 233 if ((space != NULL) && (lineNum < (LINES - 5))) { 234 ClearLine(lineNum); 235 if (space->partNum == -1) { // space is empty 236 move(lineNum, 12); 237 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str()); 238 move(lineNum, 24); 239 printw("free space"); 240 } else { // space holds a partition 241 move(lineNum, 3); 242 printw("%d", space->partNum + 1); 243 move(lineNum, 12); 244 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str()); 245 move(lineNum, 24); 246 printw(space->origPart->GetTypeName().c_str()); 247 move(lineNum, 50); 248 #ifdef USE_UTF16 249 space->origPart->GetDescription().extract(0, 39, temp, 39); 250 printw(temp); 251 #else 252 printw(space->origPart->GetDescription().c_str()); 253 #endif 254 } // if/else 255 } // if 256 return space; 257} // GPTDataCurses::ShowSpace 258 259// Display the partitions, being sure that the space #selected is displayed 260// and highlighting that space. 261// Returns the number of the space being shown (should be selected, but will 262// be -1 if something weird happens) 263int GPTDataCurses::DisplayParts(int selected) { 264 int lineNum = 5, i = 0, retval = -1, numToShow, pageNum; 265 string theLine; 266 267 move(lineNum++, 0); 268 theLine = "Part. # Size Partition Type Partition Name"; 269 printw(theLine.c_str()); 270 move(lineNum++, 0); 271 theLine = "----------------------------------------------------------------"; 272 printw(theLine.c_str()); 273 numToShow = LINES - RESERVED_TOP - RESERVED_BOTTOM; 274 pageNum = selected / numToShow; 275 for (i = pageNum * numToShow; i <= (pageNum + 1) * numToShow - 1; i++) { 276 if (i < numSpaces) { // real space; show it 277 if (i == selected) { 278 currentSpaceNum = i; 279 if (displayType == USE_CURSES) { 280 attron(A_REVERSE); 281 currentSpace = ShowSpace(i, lineNum++); 282 attroff(A_REVERSE); 283 } else { 284 currentSpace = ShowSpace(i, lineNum); 285 move(lineNum++, 0); 286 printw(">"); 287 } 288 DisplayOptions(i); 289 retval = selected; 290 } else { 291 ShowSpace(i, lineNum++); 292 } 293 } else { // blank in display 294 ClearLine(lineNum++); 295 } // if/else 296 } // for 297 refresh(); 298 return retval; 299} // GPTDataCurses::DisplayParts() 300 301/********************************************** 302 * * 303 * Functions corresponding to main menu items * 304 * * 305 **********************************************/ 306 307// Delete the specified partition and re-detect partitions and spaces.... 308void GPTDataCurses::DeletePartition(int partNum) { 309 if (!GPTData::DeletePartition(partNum)) 310 Report("Could not delete partition!"); 311 IdentifySpaces(); 312 if (currentSpaceNum >= numSpaces) { 313 currentSpaceNum = numSpaces - 1; 314 currentSpace = lastSpace; 315 } // if 316} // GPTDataCurses::DeletePartition() 317 318// Displays information on the specified partition 319void GPTDataCurses::ShowInfo(int partNum) { 320 uint64_t size; 321#ifdef USE_UTF16 322 char temp[NAME_SIZE + 1]; 323#endif 324 325 clear(); 326 move(2, (COLS - 29) / 2); 327 printw("Information for partition #%d\n\n", partNum + 1); 328 printw("Partition GUID code: %s (%s)\n", partitions[partNum].GetType().AsString().c_str(), 329 partitions[partNum].GetTypeName().c_str()); 330 printw("Partition unique GUID: %s\n", partitions[partNum].GetUniqueGUID().AsString().c_str()); 331 printw("First sector: %lld (at %s)\n", partitions[partNum].GetFirstLBA(), 332 BytesToIeee(partitions[partNum].GetFirstLBA(), blockSize).c_str()); 333 printw("Last sector: %lld (at %s)\n", partitions[partNum].GetLastLBA(), 334 BytesToIeee(partitions[partNum].GetLastLBA(), blockSize).c_str()); 335 size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA(); 336 printw("Partition size: %lld sectors (%s)\n", size, BytesToIeee(size, blockSize).c_str()); 337 printw("Attribute flags: %016x\n", partitions[partNum].GetAttributes().GetAttributes()); 338 #ifdef USE_UTF16 339 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE ); 340 printw("Partition name: '%s'\n", temp); 341 #else 342 printw("Partition name: '%s'\n", partitions[partNum].GetDescription().c_str()); 343 #endif 344 PromptToContinue(); 345} // GPTDataCurses::ShowInfo() 346 347// Prompt for and change a partition's name.... 348void GPTDataCurses::ChangeName(int partNum) { 349 char temp[NAME_SIZE + 1]; 350 351 if (ValidPartNum(partNum)) { 352 move(LINES - 4, 0); 353 clrtobot(); 354 move(LINES - 4, 0); 355 #ifdef USE_UTF16 356 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE ); 357 printw("Current partition name is '%s'\n", temp); 358 #else 359 printw("Current partition name is '%s'\n", partitions[partNum].GetDescription().c_str()); 360 #endif 361 printw("Enter new partition name, or <Enter> to use the current name:\n"); 362 echo(); 363 getnstr(temp, NAME_SIZE ); 364 partitions[partNum].SetName((string) temp); 365 noecho(); 366 } // if 367} // GPTDataCurses::ChangeName() 368 369// Change the partition's type code.... 370void GPTDataCurses::ChangeType(int partNum) { 371 char temp[80] = "L\0"; 372 PartType tempType; 373 374 echo(); 375 do { 376 move(LINES - 4, 0); 377 clrtobot(); 378 move(LINES - 4, 0); 379 printw("Current type is %04x (%s)\n", partitions[partNum].GetType().GetHexType(), partitions[partNum].GetTypeName().c_str()); 380 printw("Hex code or GUID (L to show codes, Enter = %04x): ", partitions[partNum].GetType().GetHexType()); 381 getnstr(temp, 79); 382 if ((temp[0] == 'L') || (temp[0] == 'l')) { 383 ShowTypes(); 384 } else { 385 if (temp[0] == '\0') 386 tempType = partitions[partNum].GetType().GetHexType(); 387 tempType = temp; 388 partitions[partNum].SetType(tempType); 389 } // if 390 } while ((temp[0] == 'L') || (temp[0] == 'l') || (partitions[partNum].GetType() == (GUIDData) "0x0000")); 391 noecho(); 392} // GPTDataCurses::ChangeType 393 394// Sets the partition alignment value 395void GPTDataCurses::SetAlignment(void) { 396 int alignment; 397 398 move(LINES - 4, 0); 399 clrtobot(); 400 printw("Current partition alignment, in sectors, is %d.", GetAlignment()); 401 do { 402 move(LINES - 3, 0); 403 printw("Type new alignment value, in sectors: "); 404 echo(); 405 scanw("%d", &alignment); 406 noecho(); 407 } while ((alignment == 0) || (alignment > MAX_ALIGNMENT)); 408 GPTData::SetAlignment(alignment); 409} // GPTDataCurses::SetAlignment() 410 411// Verify the data structures. Note that this function leaves curses mode and 412// relies on the underlying GPTData::Verify() function to report on problems 413void GPTDataCurses::Verify(void) { 414 char junk; 415 416 def_prog_mode(); 417 endwin(); 418 GPTData::Verify(); 419 cout << "\nPress the <Enter> key to continue: "; 420 cin.get(junk); 421 reset_prog_mode(); 422 refresh(); 423} // GPTDataCurses::Verify() 424 425// Create a new partition in the space pointed to by currentSpace. 426void GPTDataCurses::MakeNewPart(void) { 427 uint64_t size, newFirstLBA = 0, newLastLBA = 0; 428 int partNum; 429 char inLine[80]; 430 431 move(LINES - 4, 0); 432 clrtobot(); 433 while ((newFirstLBA < currentSpace->firstLBA) || (newFirstLBA > currentSpace->lastLBA)) { 434 newFirstLBA = currentSpace->firstLBA; 435 move(LINES - 4, 0); 436 clrtoeol(); 437 newFirstLBA = currentSpace->firstLBA; 438 Align(&newFirstLBA); 439 printw("First sector (%lld-%lld, default = %lld): ", newFirstLBA, currentSpace->lastLBA, newFirstLBA); 440 echo(); 441 getnstr(inLine, 79); 442 noecho(); 443 newFirstLBA = IeeeToInt(inLine, blockSize, currentSpace->firstLBA, currentSpace->lastLBA, newFirstLBA); 444 Align(&newFirstLBA); 445 } // while 446 size = currentSpace->lastLBA - newFirstLBA + 1; 447 while ((newLastLBA > currentSpace->lastLBA) || (newLastLBA < newFirstLBA)) { 448 move(LINES - 3, 0); 449 clrtoeol(); 450 printw("Size in sectors or {KMGTP} (default = %lld): ", size); 451 echo(); 452 getnstr(inLine, 79); 453 noecho(); 454 newLastLBA = newFirstLBA + IeeeToInt(inLine, blockSize, 1, size, size) - 1; 455 } // while 456 partNum = FindFirstFreePart(); 457 if (CreatePartition(partNum, newFirstLBA, newLastLBA)) { // created OK; set type code & name.... 458 ChangeType(partNum); 459 ChangeName(partNum); 460 } else { 461 Report("Error creating partition!"); 462 } // if/else 463} // GPTDataCurses::MakeNewPart() 464 465// Prompt user for permission to save data and, if it's given, do so! 466void GPTDataCurses::SaveData(void) { 467 string answer = ""; 468 char inLine[80]; 469 470 move(LINES - 4, 0); 471 clrtobot(); 472 move (LINES - 2, 14); 473 printw("Warning!! This may destroy data on your disk!"); 474 echo(); 475 while ((answer != "yes") && (answer != "no")) { 476 move (LINES - 4, 2); 477 printw("Are you sure you want to write the partition table to disk? (yes or no): "); 478 getnstr(inLine, 79); 479 answer = inLine; 480 if ((answer != "yes") && (answer != "no")) { 481 move(LINES - 2, 0); 482 clrtoeol(); 483 move(LINES - 2, 14); 484 printw("Please enter 'yes' or 'no'"); 485 } // if 486 } // while() 487 noecho(); 488 if (answer == "yes") { 489 if (SaveGPTData(1)) { 490 if (!myDisk.DiskSync()) 491 Report("The kernel may be using the old partition table. Reboot to use the new\npartition table!"); 492 } else { 493 Report("Problem saving data! Your partition table may be damaged!"); 494 } 495 } 496} // GPTDataCurses::SaveData() 497 498// Back up the partition table, prompting user for a filename.... 499void GPTDataCurses::Backup(void) { 500 char inLine[80]; 501 502 ClearBottom(); 503 move(LINES - 3, 0); 504 printw("Enter backup filename to save: "); 505 echo(); 506 getnstr(inLine, 79); 507 noecho(); 508 SaveGPTBackup(inLine); 509} // GPTDataCurses::Backup() 510 511// Load a GPT backup from a file 512void GPTDataCurses::LoadBackup(void) { 513 char inLine[80]; 514 515 ClearBottom(); 516 move(LINES - 3, 0); 517 printw("Enter backup filename to load: "); 518 echo(); 519 getnstr(inLine, 79); 520 noecho(); 521 if (!LoadGPTBackup(inLine)) 522 Report("Restoration failed!"); 523 IdentifySpaces(); 524} // GPTDataCurses::LoadBackup() 525 526// Display some basic help information 527void GPTDataCurses::ShowHelp(void) { 528 int i = 0; 529 530 clear(); 531 move(0, (COLS - 22) / 2); 532 printw("Help screen for cgdisk"); 533 move(2, 0); 534 printw("This is cgdisk, a curses-based disk partitioning program. You can use it\n"); 535 printw("to create, delete, and modify partitions on your hard disk.\n\n"); 536 attron(A_BOLD); 537 printw("Use cgdisk only on GUID Partition Table (GPT) disks!\n"); 538 attroff(A_BOLD); 539 printw("Use cfdisk on Master Boot Record (MBR) disks.\n\n"); 540 printw("Command Meaning\n"); 541 printw("------- -------\n"); 542 while (menuMain[i].key != 0) { 543 printw(" %c %s\n", menuMain[i].key, menuMain[i].desc.c_str()); 544 i++; 545 } // while() 546 PromptToContinue(); 547} // GPTDataCurses::ShowHelp() 548 549/************************************ 550 * * 551 * User input and menuing functions * 552 * * 553 ************************************/ 554 555// Change the currently-selected space.... 556void GPTDataCurses::ChangeSpaceSelection(int delta) { 557 if (currentSpace != NULL) { 558 while ((delta > 0) && (currentSpace->nextSpace != NULL)) { 559 currentSpace = currentSpace->nextSpace; 560 delta--; 561 currentSpaceNum++; 562 } // while 563 while ((delta < 0) && (currentSpace->prevSpace != NULL)) { 564 currentSpace = currentSpace->prevSpace; 565 delta++; 566 currentSpaceNum--; 567 } // while 568 } // if 569 // Below will hopefully never be true; bad counting error (bug), so reset to 570 // the first Space as a failsafe.... 571 if (DisplayParts(currentSpaceNum) != currentSpaceNum) { 572 currentSpaceNum = 0; 573 currentSpace = firstSpace; 574 DisplayParts(currentSpaceNum); 575 } // if 576} // GPTDataCurses 577 578// Move option selection left or right.... 579void GPTDataCurses::MoveSelection(int delta) { 580 int newKeyNum; 581 582 // Begin with a sanity check to ensure a valid key is selected.... 583 if (whichOptions.find(currentKey) == string::npos) 584 currentKey = 'n'; 585 newKeyNum = whichOptions.find(currentKey); 586 newKeyNum += delta; 587 if (newKeyNum < 0) 588 newKeyNum = whichOptions.length() - 1; 589 newKeyNum %= whichOptions.length(); 590 currentKey = whichOptions[newKeyNum]; 591 DisplayOptions(currentKey); 592} // GPTDataCurses::MoveSelection() 593 594// Show user's options. Refers to currentSpace to determine which options to show. 595// Highlights the option with the key selectedKey; or a default if that's invalid. 596void GPTDataCurses::DisplayOptions(char selectedKey) { 597 uint i, j = 0, firstLine, numPerLine; 598 string optionName, optionDesc = ""; 599 600 if (currentSpace != NULL) { 601 if (currentSpace->partNum == -1) { // empty space is selected 602 whichOptions = EMPTY_SPACE_OPTIONS; 603 if (whichOptions.find(selectedKey) == string::npos) 604 selectedKey = 'n'; 605 } else { // a partition is selected 606 whichOptions = PARTITION_OPTIONS; 607 if (whichOptions.find(selectedKey) == string::npos) 608 selectedKey = 't'; 609 } // if/else 610 611 firstLine = LINES - 4; 612 numPerLine = (COLS - 8) / 12; 613 ClearBottom(); 614 move(firstLine, 0); 615 for (i = 0; i < whichOptions.length(); i++) { 616 optionName = ""; 617 for (j = 0; menuMain[j].key; j++) { 618 if (menuMain[j].key == whichOptions[i]) { 619 optionName = menuMain[j].name; 620 if (whichOptions[i] == selectedKey) 621 optionDesc = menuMain[j].desc; 622 } // if 623 } // for 624 move(firstLine + i / numPerLine, (i % numPerLine) * 12 + 4); 625 if (whichOptions[i] == selectedKey) { 626 attron(A_REVERSE); 627 printw("[ %s ]", optionName.c_str()); 628 attroff(A_REVERSE); 629 } else { 630 printw("[ %s ]", optionName.c_str()); 631 } // if/else 632 } // for 633 move(LINES - 1, (COLS - optionDesc.length()) / 2); 634 printw(optionDesc.c_str()); 635 currentKey = selectedKey; 636 } // if 637} // GPTDataCurses::DisplayOptions() 638 639// Accept user input and process it. Returns when the program should terminate. 640void GPTDataCurses::AcceptInput() { 641 int inputKey, exitNow = 0; 642 643 do { 644 refresh(); 645 inputKey = getch(); 646 switch (inputKey) { 647 case KEY_UP: 648 ChangeSpaceSelection(-1); 649 break; 650 case KEY_DOWN: 651 ChangeSpaceSelection(+1); 652 break; 653 case 339: // page up key 654 ChangeSpaceSelection(RESERVED_TOP + RESERVED_BOTTOM - LINES); 655 break; 656 case 338: // page down key 657 ChangeSpaceSelection(LINES - RESERVED_TOP - RESERVED_BOTTOM); 658 break; 659 case KEY_LEFT: 660 MoveSelection(-1); 661 break; 662 case KEY_RIGHT: 663 MoveSelection(+1); 664 break; 665 case KEY_ENTER: case 13: 666 exitNow = Dispatch(currentKey); 667 break; 668 case 27: // escape key 669 exitNow = 1; 670 break; 671 default: 672 exitNow = Dispatch(inputKey); 673 break; 674 } // switch() 675 } while (!exitNow); 676} // GPTDataCurses::AcceptInput() 677 678// Operation has been selected, so do it. Returns 1 if the program should 679// terminate on return from this program, 0 otherwise. 680int GPTDataCurses::Dispatch(char operation) { 681 int exitNow = 0; 682 683 switch (operation) { 684 case 'a': case 'A': 685 SetAlignment(); 686 break; 687 case 'b': case 'B': 688 Backup(); 689 break; 690 case 'd': case 'D': 691 if (ValidPartNum(currentSpace->partNum)) 692 DeletePartition(currentSpace->partNum); 693 break; 694 case 'h': case 'H': 695 ShowHelp(); 696 break; 697 case 'i': case 'I': 698 if (ValidPartNum(currentSpace->partNum)) 699 ShowInfo(currentSpace->partNum); 700 break; 701 case 'l': case 'L': 702 LoadBackup(); 703 break; 704 case 'm': case 'M': 705 if (ValidPartNum(currentSpace->partNum)) 706 ChangeName(currentSpace->partNum); 707 break; 708 case 'n': case 'N': 709 if (currentSpace->partNum < 0) { 710 MakeNewPart(); 711 IdentifySpaces(); 712 } // if 713 break; 714 case 'q': case 'Q': 715 exitNow = 1; 716 break; 717 case 't': case 'T': 718 if (ValidPartNum(currentSpace->partNum)) 719 ChangeType(currentSpace->partNum); 720 break; 721 case 'v': case 'V': 722 Verify(); 723 break; 724 case 'w': case 'W': 725 SaveData(); 726 break; 727 default: 728 break; 729 } // switch() 730 DrawMenu(); 731 return exitNow; 732} // GPTDataCurses::Dispatch() 733 734// Draws the main menu 735void GPTDataCurses::DrawMenu(void) { 736 string title="cgdisk "; 737 title += GPTFDISK_VERSION; 738 string drive="Disk Drive: "; 739 drive += device; 740 ostringstream size; 741 742 size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize); 743 744 clear(); 745 move(0, (COLS - title.length()) / 2); 746 printw(title.c_str()); 747 move(2, (COLS - drive.length()) / 2); 748 printw(drive.c_str()); 749 move(3, (COLS - size.str().length()) / 2); 750 printw(size.str().c_str()); 751 DisplayParts(currentSpaceNum); 752} // DrawMenu 753 754int GPTDataCurses::MainMenu(void) { 755 if (((LINES - RESERVED_TOP - RESERVED_BOTTOM) < 2) || (COLS < 80)) { 756 Report("Display is too small; it must be at least 80 x 14 characters!"); 757 } else { 758 if (GPTData::Verify() > 0) 759 Report("Warning! Problems found on disk! Use the Verify function to learn more.\n" 760 "Using gdisk or some other program may be necessary to repair the problems."); 761 IdentifySpaces(); 762 currentSpaceNum = 0; 763 DrawMenu(); 764 AcceptInput(); 765 } // if/else 766 endwin(); 767 return 0; 768} // GPTDataCurses::MainMenu 769 770/*********************************************************** 771 * * 772 * Non-class support functions (mostly related to ncurses) * 773 * * 774 ***********************************************************/ 775 776// Clears the specified line of all data.... 777void ClearLine(int lineNum) { 778 move(lineNum, 0); 779 clrtoeol(); 780} // ClearLine() 781 782// Clear the last few lines of the display 783void ClearBottom(void) { 784 move(LINES - RESERVED_BOTTOM, 0); 785 clrtobot(); 786} // ClearBottom() 787 788void PromptToContinue(void) { 789 ClearBottom(); 790 move(LINES - 2, (COLS - 29) / 2); 791 printw("Press any key to continue...."); 792 cbreak(); 793 getch(); 794} // PromptToContinue() 795 796// Display one line of text on the screen and prompt to press any key to continue. 797void Report(string theText) { 798 clear(); 799 move(0, 0); 800 printw(theText.c_str()); 801 move(LINES - 2, (COLS - 29) / 2); 802 printw("Press any key to continue...."); 803 cbreak(); 804 getch(); 805} // Report() 806 807// Displays all the partition type codes and then prompts to continue.... 808// NOTE: This function temporarily exits curses mode as a matter of 809// convenience. 810void ShowTypes(void) { 811 PartType tempType; 812 char junk; 813 814 def_prog_mode(); 815 endwin(); 816 tempType.ShowAllTypes(LINES - 3); 817 cout << "\nPress the <Enter> key to continue: "; 818 cin.get(junk); 819 reset_prog_mode(); 820 refresh(); 821} // ShowTypes() 822