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