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