blob: 84621494dc3878548dc88cebe1a9ba2991af9058 [file] [log] [blame]
srs56943860cbe2011-09-10 20:29:53 -04001/*
2 * Implementation of GPTData class derivative with curses-based text-mode
3 * interaction
4 * Copyright (C) 2011 Roderick W. Smith
srs5694d1b11e82011-09-18 21:12:28 -04005 *
srs56943860cbe2011-09-10 20:29:53 -04006 * 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.
srs5694d1b11e82011-09-18 21:12:28 -040010 *
srs56943860cbe2011-09-10 20:29:53 -040011 * 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.
srs5694d1b11e82011-09-18 21:12:28 -040015 *
srs56943860cbe2011-09-10 20:29:53 -040016 * 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.
srs5694d1b11e82011-09-18 21:12:28 -040019 *
srs56943860cbe2011-09-10 20:29:53 -040020 */
21
22#include <iostream>
23#include <string>
24#include <sstream>
25#include <ncurses.h>
26#include "gptcurses.h"
27#include "support.h"
28
29using 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
36int GPTDataCurses::numInstances = 0;
37
38GPTDataCurses::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
58GPTDataCurses::~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
70void 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.
85int 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....
104void 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.
118int 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.
149void 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.
166void 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.
181void 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.
206void 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
219Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) {
220 Space *space;
221 int i = 0;
srs56940741fa22013-01-09 12:55:40 -0500222#ifdef USE_UTF16
srs56943860cbe2011-09-10 20:29:53 -0400223 char temp[40];
srs56940741fa22013-01-09 12:55:40 -0500224#endif
srs56943860cbe2011-09-10 20:29:53 -0400225
226 space = firstSpace;
227 while ((space != NULL) && (i < spaceNum)) {
228 space = space->nextSpace;
229 i++;
230 } // while
231 if ((space != NULL) && (lineNum < (LINES - 5))) {
232 ClearLine(lineNum);
233 if (space->partNum == -1) { // space is empty
234 move(lineNum, 12);
235 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
236 move(lineNum, 24);
237 printw("free space");
238 } else { // space holds a partition
239 move(lineNum, 3);
240 printw("%d", space->partNum + 1);
241 move(lineNum, 12);
242 printw(BytesToIeee((space->lastLBA - space->firstLBA + 1), blockSize).c_str());
243 move(lineNum, 24);
244 printw(space->origPart->GetTypeName().c_str());
245 move(lineNum, 50);
246 #ifdef USE_UTF16
247 space->origPart->GetDescription().extract(0, 39, temp, 39);
248 printw(temp);
249 #else
250 printw(space->origPart->GetDescription().c_str());
251 #endif
252 } // if/else
253 } // if
254 return space;
255} // GPTDataCurses::ShowSpace
256
257// Display the partitions, being sure that the space #selected is displayed
258// and highlighting that space.
259// Returns the number of the space being shown (should be selected, but will
260// be -1 if something weird happens)
261int GPTDataCurses::DisplayParts(int selected) {
262 int lineNum = 5, i = 0, retval = -1, numToShow, pageNum;
263 string theLine;
264
265 move(lineNum++, 0);
266 theLine = "Part. # Size Partition Type Partition Name";
267 printw(theLine.c_str());
268 move(lineNum++, 0);
269 theLine = "----------------------------------------------------------------";
270 printw(theLine.c_str());
271 numToShow = LINES - RESERVED_TOP - RESERVED_BOTTOM;
272 pageNum = selected / numToShow;
273 for (i = pageNum * numToShow; i <= (pageNum + 1) * numToShow - 1; i++) {
274 if (i < numSpaces) { // real space; show it
275 if (i == selected) {
276 attron(A_REVERSE);
277 currentSpaceNum = i;
278 currentSpace = ShowSpace(i, lineNum++);
279 attroff(A_REVERSE);
280 DisplayOptions(i);
281 retval = selected;
282 } else {
283 ShowSpace(i, lineNum++);
284 }
285 } else { // blank in display
286 ClearLine(lineNum++);
287 } // if/else
288 } // for
289 refresh();
290 return retval;
291} // GPTDataCurses::DisplayParts()
292
293/**********************************************
294 * *
295 * Functions corresponding to main menu items *
296 * *
297 **********************************************/
298
299// Delete the specified partition and re-detect partitions and spaces....
300void GPTDataCurses::DeletePartition(int partNum) {
301 if (!GPTData::DeletePartition(partNum))
302 Report("Could not delete partition!");
303 IdentifySpaces();
304 if (currentSpaceNum >= numSpaces) {
305 currentSpaceNum = numSpaces - 1;
306 currentSpace = lastSpace;
307 } // if
308} // GPTDataCurses::DeletePartition()
309
310// Displays information on the specified partition
311void GPTDataCurses::ShowInfo(int partNum) {
312 uint64_t size;
srs56940741fa22013-01-09 12:55:40 -0500313#ifdef USE_UTF16
srs56943860cbe2011-09-10 20:29:53 -0400314 char temp[NAME_SIZE / 2 + 1];
srs56940741fa22013-01-09 12:55:40 -0500315#endif
srs56943860cbe2011-09-10 20:29:53 -0400316
317 clear();
318 move(2, (COLS - 29) / 2);
319 printw("Information for partition #%d\n\n", partNum + 1);
320 printw("Partition GUID code: %s (%s)\n", partitions[partNum].GetType().AsString().c_str(),
321 partitions[partNum].GetTypeName().c_str());
322 printw("Partition unique GUID: %s\n", partitions[partNum].GetUniqueGUID().AsString().c_str());
323 printw("First sector: %lld (at %s)\n", partitions[partNum].GetFirstLBA(),
324 BytesToIeee(partitions[partNum].GetFirstLBA(), blockSize).c_str());
325 printw("Last sector: %lld (at %s)\n", partitions[partNum].GetLastLBA(),
326 BytesToIeee(partitions[partNum].GetLastLBA(), blockSize).c_str());
327 size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA();
328 printw("Partition size: %lld sectors (%s)\n", size, BytesToIeee(size, blockSize).c_str());
329 printw("Attribute flags: %016x\n", partitions[partNum].GetAttributes().GetAttributes());
330 #ifdef USE_UTF16
331 partitions[partNum].GetDescription().extract(0, NAME_SIZE / 2, temp, NAME_SIZE / 2);
332 printw("Partition name: '%s'\n", temp);
333 #else
334 printw("Partition name: '%s'\n", partitions[partNum].GetDescription().c_str());
335 #endif
336 PromptToContinue();
337} // GPTDataCurses::ShowInfo()
338
339// Prompt for and change a partition's name....
340void GPTDataCurses::ChangeName(int partNum) {
341 char temp[NAME_SIZE / 2 + 1];
342
343 if (ValidPartNum(partNum)) {
344 move(LINES - 4, 0);
345 clrtobot();
346 move(LINES - 4, 0);
347 #ifdef USE_UTF16
348 partitions[partNum].GetDescription().extract(0, NAME_SIZE / 2, temp, NAME_SIZE / 2);
349 printw("Current partition name is '%s'\n", temp);
350 #else
351 printw("Current partition name is '%s'\n", partitions[partNum].GetDescription().c_str());
352 #endif
353 printw("Enter new partition name, or <Enter> to use the current name:\n");
354 echo();
355 getnstr(temp, NAME_SIZE / 2);
356 partitions[partNum].SetName((string) temp);
357 noecho();
358 } // if
359} // GPTDataCurses::ChangeName()
360
361// Change the partition's type code....
362void GPTDataCurses::ChangeType(int partNum) {
363 char temp[80] = "L\0";
364 PartType tempType;
365
366 echo();
367 do {
368 move(LINES - 4, 0);
369 clrtobot();
370 move(LINES - 4, 0);
371 printw("Current type is %04x (%s)\n", partitions[partNum].GetType().GetHexType(), partitions[partNum].GetTypeName().c_str());
372 printw("Hex code or GUID (L to show codes, Enter = %04x): ", partitions[partNum].GetType().GetHexType());
373 getnstr(temp, 79);
374 if ((temp[0] == 'L') || (temp[0] == 'l')) {
375 ShowTypes();
376 } else {
377 if (temp[0] == '\0')
378 tempType = partitions[partNum].GetType().GetHexType();
379 tempType = temp;
380 partitions[partNum].SetType(tempType);
381 } // if
382 } while ((temp[0] == 'L') || (temp[0] == 'l') || (partitions[partNum].GetType() == (GUIDData) "0x0000"));
383 noecho();
384} // GPTDataCurses::ChangeType
385
386// Sets the partition alignment value
387void GPTDataCurses::SetAlignment(void) {
388 int alignment;
389
390 move(LINES - 4, 0);
391 clrtobot();
392 printw("Current partition alignment, in sectors, is %d.", GetAlignment());
393 do {
394 move(LINES - 3, 0);
395 printw("Type new alignment value, in sectors: ");
396 echo();
397 scanw("%d", &alignment);
398 noecho();
399 } while ((alignment == 0) || (alignment > MAX_ALIGNMENT));
400 GPTData::SetAlignment(alignment);
401} // GPTDataCurses::SetAlignment()
402
403// Verify the data structures. Note that this function leaves curses mode and
404// relies on the underlying GPTData::Verify() function to report on problems
405void GPTDataCurses::Verify(void) {
406 char junk;
407
408 def_prog_mode();
409 endwin();
410 GPTData::Verify();
411 cout << "\nPress the <Enter> key to continue: ";
412 cin.get(junk);
413 reset_prog_mode();
414 refresh();
415} // GPTDataCurses::Verify()
416
417// Create a new partition in the space pointed to by currentSpace.
418void GPTDataCurses::MakeNewPart(void) {
419 uint64_t size, newFirstLBA = 0, newLastLBA = 0;
420 int partNum;
421 char inLine[80];
422
423 move(LINES - 4, 0);
424 clrtobot();
425 while ((newFirstLBA < currentSpace->firstLBA) || (newFirstLBA > currentSpace->lastLBA)) {
426 newFirstLBA = currentSpace->firstLBA;
427 move(LINES - 4, 0);
428 clrtoeol();
429 newFirstLBA = currentSpace->firstLBA;
430 Align(&newFirstLBA);
431 printw("First sector (%lld-%lld, default = %lld): ", newFirstLBA, currentSpace->lastLBA, newFirstLBA);
432 echo();
433 getnstr(inLine, 79);
434 noecho();
435 newFirstLBA = IeeeToInt(inLine, blockSize, currentSpace->firstLBA, currentSpace->lastLBA, newFirstLBA);
436 Align(&newFirstLBA);
437 } // while
438 size = currentSpace->lastLBA - newFirstLBA + 1;
439 while ((newLastLBA > currentSpace->lastLBA) || (newLastLBA < newFirstLBA)) {
440 move(LINES - 3, 0);
441 clrtoeol();
442 printw("Size in sectors or {KMGTP} (default = %lld): ", size);
443 echo();
444 getnstr(inLine, 79);
445 noecho();
446 newLastLBA = newFirstLBA + IeeeToInt(inLine, blockSize, 1, size, size) - 1;
447 } // while
448 partNum = FindFirstFreePart();
449 if (CreatePartition(partNum, newFirstLBA, newLastLBA)) { // created OK; set type code & name....
450 ChangeType(partNum);
451 ChangeName(partNum);
452 } else {
453 Report("Error creating partition!");
454 } // if/else
455} // GPTDataCurses::MakeNewPart()
456
457// Prompt user for permission to save data and, if it's given, do so!
458void GPTDataCurses::SaveData(void) {
459 string answer = "";
460 char inLine[80];
461
462 move(LINES - 4, 0);
463 clrtobot();
464 move (LINES - 2, 14);
465 printw("Warning!! This may destroy data on your disk!");
466 echo();
467 while ((answer != "yes") && (answer != "no")) {
468 move (LINES - 4, 2);
469 printw("Are you sure you want to write the partition table to disk? (yes or no): ");
470 getnstr(inLine, 79);
471 answer = inLine;
472 if ((answer != "yes") && (answer != "no")) {
473 move(LINES - 2, 0);
474 clrtoeol();
475 move(LINES - 2, 14);
476 printw("Please enter 'yes' or 'no'");
477 } // if
478 } // while()
479 noecho();
480 if (answer == "yes") {
481 if (SaveGPTData(1)) {
482 if (!myDisk.DiskSync())
483 Report("The kernel may be using the old partition table. Reboot to use the new\npartition table!");
484 } else {
485 Report("Problem saving data! Your partition table may be damaged!");
486 }
487 }
488} // GPTDataCurses::SaveData()
489
490// Back up the partition table, prompting user for a filename....
491void GPTDataCurses::Backup(void) {
492 char inLine[80];
493
494 ClearBottom();
495 move(LINES - 3, 0);
496 printw("Enter backup filename to save: ");
497 echo();
498 getnstr(inLine, 79);
499 noecho();
500 SaveGPTBackup(inLine);
501} // GPTDataCurses::Backup()
502
503// Load a GPT backup from a file
504void GPTDataCurses::LoadBackup(void) {
505 char inLine[80];
506
507 ClearBottom();
508 move(LINES - 3, 0);
509 printw("Enter backup filename to load: ");
510 echo();
511 getnstr(inLine, 79);
512 noecho();
513 if (!LoadGPTBackup(inLine))
514 Report("Restoration failed!");
515 IdentifySpaces();
516} // GPTDataCurses::LoadBackup()
517
518// Display some basic help information
519void GPTDataCurses::ShowHelp(void) {
520 int i = 0;
521
522 clear();
523 move(0, (COLS - 22) / 2);
524 printw("Help screen for cgdisk");
525 move(2, 0);
526 printw("This is cgdisk, a curses-based disk partitioning program. You can use it\n");
527 printw("to create, delete, and modify partitions on your hard disk.\n\n");
528 attron(A_BOLD);
529 printw("Use cgdisk only on GUID Partition Table (GPT) disks!\n");
530 attroff(A_BOLD);
531 printw("Use cfdisk on Master Boot Record (MBR) disks.\n\n");
532 printw("Command Meaning\n");
533 printw("------- -------\n");
534 while (menuMain[i].key != 0) {
535 printw(" %c %s\n", menuMain[i].key, menuMain[i].desc.c_str());
536 i++;
537 } // while()
538 PromptToContinue();
539} // GPTDataCurses::ShowHelp()
540
541/************************************
542 * *
543 * User input and menuing functions *
544 * *
545 ************************************/
546
547// Change the currently-selected space....
548void GPTDataCurses::ChangeSpaceSelection(int delta) {
549 if (currentSpace != NULL) {
550 while ((delta > 0) && (currentSpace->nextSpace != NULL)) {
551 currentSpace = currentSpace->nextSpace;
552 delta--;
553 currentSpaceNum++;
554 } // while
555 while ((delta < 0) && (currentSpace->prevSpace != NULL)) {
556 currentSpace = currentSpace->prevSpace;
557 delta++;
558 currentSpaceNum--;
559 } // while
560 } // if
561 // Below will hopefully never be true; bad counting error (bug), so reset to
562 // the first Space as a failsafe....
563 if (DisplayParts(currentSpaceNum) != currentSpaceNum) {
564 currentSpaceNum = 0;
565 currentSpace = firstSpace;
566 DisplayParts(currentSpaceNum);
567 } // if
568} // GPTDataCurses
569
570// Move option selection left or right....
571void GPTDataCurses::MoveSelection(int delta) {
572 int newKeyNum;
573
574 // Begin with a sanity check to ensure a valid key is selected....
575 if (whichOptions.find(currentKey) == string::npos)
576 currentKey = 'n';
577 newKeyNum = whichOptions.find(currentKey);
578 newKeyNum += delta;
579 if (newKeyNum < 0)
580 newKeyNum = whichOptions.length() - 1;
581 newKeyNum %= whichOptions.length();
582 currentKey = whichOptions[newKeyNum];
583 DisplayOptions(currentKey);
584} // GPTDataCurses::MoveSelection()
585
586// Show user's options. Refers to currentSpace to determine which options to show.
587// Highlights the option with the key selectedKey; or a default if that's invalid.
588void GPTDataCurses::DisplayOptions(char selectedKey) {
589 uint i, j = 0, firstLine, numPerLine;
590 string optionName, optionDesc = "";
591
592 if (currentSpace != NULL) {
593 if (currentSpace->partNum == -1) { // empty space is selected
594 whichOptions = EMPTY_SPACE_OPTIONS;
595 if (whichOptions.find(selectedKey) == string::npos)
596 selectedKey = 'n';
597 } else { // a partition is selected
598 whichOptions = PARTITION_OPTIONS;
599 if (whichOptions.find(selectedKey) == string::npos)
600 selectedKey = 't';
601 } // if/else
602
603 firstLine = LINES - 4;
604 numPerLine = (COLS - 8) / 12;
605 ClearBottom();
606 move(firstLine, 0);
607 for (i = 0; i < whichOptions.length(); i++) {
608 optionName = "";
609 for (j = 0; menuMain[j].key; j++) {
610 if (menuMain[j].key == whichOptions[i]) {
611 optionName = menuMain[j].name;
612 if (whichOptions[i] == selectedKey)
613 optionDesc = menuMain[j].desc;
614 } // if
615 } // for
616 move(firstLine + i / numPerLine, (i % numPerLine) * 12 + 4);
617 if (whichOptions[i] == selectedKey) {
618 attron(A_REVERSE);
619 printw("[ %s ]", optionName.c_str());
620 attroff(A_REVERSE);
621 } else {
622 printw("[ %s ]", optionName.c_str());
623 } // if/else
624 } // for
625 move(LINES - 1, (COLS - optionDesc.length()) / 2);
626 printw(optionDesc.c_str());
627 currentKey = selectedKey;
628 } // if
629} // GPTDataCurses::DisplayOptions()
630
631// Accept user input and process it. Returns when the program should terminate.
632void GPTDataCurses::AcceptInput() {
633 int inputKey, exitNow = 0;
634
635 do {
636 refresh();
637 inputKey = getch();
638 switch (inputKey) {
639 case KEY_UP:
640 ChangeSpaceSelection(-1);
641 break;
642 case KEY_DOWN:
643 ChangeSpaceSelection(+1);
644 break;
645 case 339: // page up key
646 ChangeSpaceSelection(RESERVED_TOP + RESERVED_BOTTOM - LINES);
647 break;
648 case 338: // page down key
649 ChangeSpaceSelection(LINES - RESERVED_TOP - RESERVED_BOTTOM);
650 break;
651 case KEY_LEFT:
652 MoveSelection(-1);
653 break;
654 case KEY_RIGHT:
655 MoveSelection(+1);
656 break;
657 case KEY_ENTER: case 13:
658 exitNow = Dispatch(currentKey);
659 break;
660 case 27: // escape key
661 exitNow = 1;
662 break;
663 default:
664 exitNow = Dispatch(inputKey);
665 break;
666 } // switch()
667 } while (!exitNow);
668} // GPTDataCurses::AcceptInput()
669
670// Operation has been selected, so do it. Returns 1 if the program should
671// terminate on return from this program, 0 otherwise.
672int GPTDataCurses::Dispatch(char operation) {
673 int exitNow = 0;
674
675 switch (operation) {
676 case 'a': case 'A':
677 SetAlignment();
678 break;
679 case 'b': case 'B':
680 Backup();
681 break;
682 case 'd': case 'D':
683 if (ValidPartNum(currentSpace->partNum))
684 DeletePartition(currentSpace->partNum);
685 break;
686 case 'h': case 'H':
687 ShowHelp();
688 break;
689 case 'i': case 'I':
690 if (ValidPartNum(currentSpace->partNum))
691 ShowInfo(currentSpace->partNum);
692 break;
693 case 'l': case 'L':
694 LoadBackup();
695 break;
696 case 'm': case 'M':
697 if (ValidPartNum(currentSpace->partNum))
698 ChangeName(currentSpace->partNum);
699 break;
700 case 'n': case 'N':
701 if (currentSpace->partNum < 0) {
702 MakeNewPart();
703 IdentifySpaces();
704 } // if
705 break;
706 case 'q': case 'Q':
707 exitNow = 1;
708 break;
709 case 't': case 'T':
710 if (ValidPartNum(currentSpace->partNum))
711 ChangeType(currentSpace->partNum);
712 break;
713 case 'v': case 'V':
714 Verify();
715 break;
716 case 'w': case 'W':
717 SaveData();
718 break;
719 default:
720 break;
721 } // switch()
722 DrawMenu();
723 return exitNow;
724} // GPTDataCurses::Dispatch()
725
726// Draws the main menu
727void GPTDataCurses::DrawMenu(void) {
728 string title="cgdisk ";
729 title += GPTFDISK_VERSION;
730 string drive="Disk Drive: ";
731 drive += device;
732 ostringstream size;
733 size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize);
734
735 clear();
736 move(0, (COLS - title.length()) / 2);
737 printw(title.c_str());
738 move(2, (COLS - drive.length()) / 2);
739 printw(drive.c_str());
740 move(3, (COLS - size.str().length()) / 2);
741 printw(size.str().c_str());
742 DisplayParts(currentSpaceNum);
743} // DrawMenu
744
745int GPTDataCurses::MainMenu(void) {
746 if (((LINES - RESERVED_TOP - RESERVED_BOTTOM) < 2) || (COLS < 80)) {
747 Report("Display is too small; it must be at least 80 x 14 characters!");
748 } else {
749 if (GPTData::Verify() > 0)
750 Report("Warning! Problems found on disk! Use the Verify function to learn more.\n"
751 "Using gdisk or some other program may be necessary to repair the problems.");
752 IdentifySpaces();
753 currentSpaceNum = 0;
754 DrawMenu();
755 AcceptInput();
756 } // if/else
757 endwin();
758 return 0;
759} // GPTDataCurses::MainMenu
760
761/***********************************************************
762 * *
763 * Non-class support functions (mostly related to ncurses) *
764 * *
765 ***********************************************************/
766
767// Clears the specified line of all data....
768void ClearLine(int lineNum) {
769 move(lineNum, 0);
770 clrtoeol();
771} // ClearLine()
772
773// Clear the last few lines of the display
774void ClearBottom(void) {
775 move(LINES - RESERVED_BOTTOM, 0);
776 clrtobot();
777} // ClearBottom()
778
779void PromptToContinue(void) {
780 ClearBottom();
781 move(LINES - 2, (COLS - 29) / 2);
782 printw("Press any key to continue....");
783 cbreak();
784 getch();
785} // PromptToContinue()
786
787// Display one line of text on the screen and prompt to press any key to continue.
788void Report(string theText) {
789 clear();
790 move(0, 0);
791 printw(theText.c_str());
792 move(LINES - 2, (COLS - 29) / 2);
793 printw("Press any key to continue....");
794 cbreak();
795 getch();
796} // Report()
797
798// Displays all the partition type codes and then prompts to continue....
799// NOTE: This function temporarily exits curses mode as a matter of
800// convenience.
801void ShowTypes(void) {
802 PartType tempType;
803 char junk;
804
805 def_prog_mode();
806 endwin();
807 tempType.ShowAllTypes();
808 cout << "\nPress the <Enter> key to continue: ";
809 cin.get(junk);
810 reset_prog_mode();
811 refresh();
812} // ShowTypes()