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
29 using 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
36 int GPTDataCurses::numInstances = 0;
37
GPTDataCurses(void)38 GPTDataCurses::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
~GPTDataCurses(void)60 GPTDataCurses::~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
EmptySpaces(void)72 void 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.
MakeSpacesFromParts(void)87 int 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....
AddEmptySpace(uint64_t firstLBA,uint64_t lastLBA)106 void 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.
AddEmptySpaces(void)120 int 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.
UnlinkSpace(Space * theSpace)151 void 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.
LinkToEnd(Space * theSpace)168 void 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.
SortSpaces(void)183 void 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.
IdentifySpaces(void)208 void 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
ShowSpace(int spaceNum,int lineNum)221 Space* 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)
DisplayParts(int selected)263 int 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....
DeletePartition(int partNum)308 void 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
ShowInfo(int partNum)319 void 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....
ChangeName(int partNum)348 void 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....
ChangeType(int partNum)370 void 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
SetAlignment(void)395 void 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
Verify(void)413 void 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.
MakeNewPart(void)426 void 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!
SaveData(void)466 void 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....
Backup(void)499 void 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
LoadBackup(void)512 void 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
ShowHelp(void)527 void 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....
ChangeSpaceSelection(int delta)556 void 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....
MoveSelection(int delta)579 void 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.
DisplayOptions(char selectedKey)596 void 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.
AcceptInput()640 void 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.
Dispatch(char operation)680 int 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
DrawMenu(void)735 void 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
MainMenu(void)754 int 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....
ClearLine(int lineNum)777 void ClearLine(int lineNum) {
778 move(lineNum, 0);
779 clrtoeol();
780 } // ClearLine()
781
782 // Clear the last few lines of the display
ClearBottom(void)783 void ClearBottom(void) {
784 move(LINES - RESERVED_BOTTOM, 0);
785 clrtobot();
786 } // ClearBottom()
787
PromptToContinue(void)788 void 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.
Report(string theText)797 void 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.
ShowTypes(void)810 void 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