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