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