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