xref: /aosp_15_r20/external/gptfdisk/gptcurses.cc (revision 57696d54d05c64fd1b1787f8371dbcf104911cfb)
1 /*
2  *    Implementation of GPTData class derivative with curses-based text-mode
3  *    interaction
4  *    Copyright (C) 2011-2022 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 
34 using 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 
41 int GPTDataCurses::numInstances = 0;
42 
GPTDataCurses(void)43 GPTDataCurses::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 
~GPTDataCurses(void)65 GPTDataCurses::~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 
EmptySpaces(void)77 void 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.
MakeSpacesFromParts(void)92 int 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....
AddEmptySpace(uint64_t firstLBA,uint64_t lastLBA)111 void 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.
AddEmptySpaces(void)125 int 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.
UnlinkSpace(Space * theSpace)156 void 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.
LinkToEnd(Space * theSpace)173 void 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.
SortSpaces(void)188 void 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.
IdentifySpaces(void)213 void 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
ShowSpace(int spaceNum,int lineNum)226 Space* 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)
DisplayParts(int selected)268 int 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....
DeletePartition(int partNum)313 void 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
ShowInfo(int partNum)324 void 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....
ChangeName(int partNum)353 void 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....
ChangeType(int partNum)375 void 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
SetAlignment(void)400 void 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
Verify(void)419 void 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.
MakeNewPart(void)432 void 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!
SaveData(void)481 void 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....
Backup(void)514 void 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
LoadBackup(void)527 void 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
ShowHelp(void)542 void 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....
ChangeSpaceSelection(int delta)571 void 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....
MoveSelection(int delta)594 void 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.
DisplayOptions(char selectedKey)611 void 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.
AcceptInput()655 void 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.
Dispatch(char operation)695 int 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
DrawMenu(void)750 void 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 
MainMenu(void)769 int 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....
ClearLine(int lineNum)792 void ClearLine(int lineNum) {
793    move(lineNum, 0);
794    clrtoeol();
795 } // ClearLine()
796 
797 // Clear the last few lines of the display
ClearBottom(void)798 void ClearBottom(void) {
799    move(LINES - RESERVED_BOTTOM, 0);
800    clrtobot();
801 } // ClearBottom()
802 
PromptToContinue(void)803 void 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.
Report(string theText)812 void 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.
ShowTypes(void)825 void 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