blob: 4ebfde126e7a212c2db2c184ec333001f0009434 [file] [log] [blame]
srs56943860cbe2011-09-10 20:29:53 -04001/*
2 * Implementation of GPTData class derivative with curses-based text-mode
3 * interaction
Rod Smith44cda472018-07-05 09:07:58 -04004 * Copyright (C) 2011-2018 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
Rosen Penev9e6016b2019-10-07 15:34:43 -070022#include <clocale>
srs56943860cbe2011-09-10 20:29:53 -040023#include <iostream>
24#include <string>
25#include <sstream>
Roderick W. Smitha9630e32015-10-07 17:06:53 -040026#include <ncurses.h>
srs56943860cbe2011-09-10 20:29:53 -040027#include "gptcurses.h"
28#include "support.h"
29
30using 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
37int GPTDataCurses::numInstances = 0;
38
39GPTDataCurses::GPTDataCurses(void) {
40 if (numInstances > 0) {
41 refresh();
42 } else {
Roderick W. Smith84aaff62014-02-17 16:17:11 -050043 setlocale( LC_ALL , "" );
srs56943860cbe2011-09-10 20:29:53 -040044 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
Roderick W. Smith1eea9b02013-07-06 22:52:58 -040058 displayType = USE_CURSES;
srs56943860cbe2011-09-10 20:29:53 -040059} // GPTDataCurses constructor
60
61GPTDataCurses::~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
73void 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.
88int 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....
107void 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.
121int 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.
152void 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.
169void 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.
184void 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.
209void 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
222Space* GPTDataCurses::ShowSpace(int spaceNum, int lineNum) {
223 Space *space;
224 int i = 0;
srs56940741fa22013-01-09 12:55:40 -0500225#ifdef USE_UTF16
srs56943860cbe2011-09-10 20:29:53 -0400226 char temp[40];
srs56940741fa22013-01-09 12:55:40 -0500227#endif
srs56943860cbe2011-09-10 20:29:53 -0400228
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)
264int 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) {
srs56943860cbe2011-09-10 20:29:53 -0400279 currentSpaceNum = i;
Roderick W. Smith1eea9b02013-07-06 22:52:58 -0400280 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 }
srs56943860cbe2011-09-10 20:29:53 -0400289 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....
309void 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
320void GPTDataCurses::ShowInfo(int partNum) {
321 uint64_t size;
srs56940741fa22013-01-09 12:55:40 -0500322#ifdef USE_UTF16
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500323 char temp[NAME_SIZE + 1];
srs56940741fa22013-01-09 12:55:40 -0500324#endif
srs56943860cbe2011-09-10 20:29:53 -0400325
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());
Roderick W. Smith13929642015-10-17 18:07:51 -0400336 size = partitions[partNum].GetLastLBA() - partitions[partNum].GetFirstLBA() + 1;
srs56943860cbe2011-09-10 20:29:53 -0400337 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
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500340 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE );
srs56943860cbe2011-09-10 20:29:53 -0400341 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....
349void GPTDataCurses::ChangeName(int partNum) {
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500350 char temp[NAME_SIZE + 1];
srs56943860cbe2011-09-10 20:29:53 -0400351
352 if (ValidPartNum(partNum)) {
353 move(LINES - 4, 0);
354 clrtobot();
355 move(LINES - 4, 0);
356 #ifdef USE_UTF16
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500357 partitions[partNum].GetDescription().extract(0, NAME_SIZE , temp, NAME_SIZE );
srs56943860cbe2011-09-10 20:29:53 -0400358 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();
Roderick W. Smith84aaff62014-02-17 16:17:11 -0500364 getnstr(temp, NAME_SIZE );
srs56943860cbe2011-09-10 20:29:53 -0400365 partitions[partNum].SetName((string) temp);
366 noecho();
367 } // if
368} // GPTDataCurses::ChangeName()
369
370// Change the partition's type code....
371void 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
396void GPTDataCurses::SetAlignment(void) {
397 int alignment;
Rod Smith9ae60192018-07-05 16:50:13 -0400398 char conversion_specifier[] = "%d";
srs56943860cbe2011-09-10 20:29:53 -0400399
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();
Rod Smith9ae60192018-07-05 16:50:13 -0400407 scanw(conversion_specifier, &alignment);
srs56943860cbe2011-09-10 20:29:53 -0400408 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
415void 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.
428void 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!
468void 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....
501void 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
514void 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
529void 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....
558void 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....
581void 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.
598void 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.
642void 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.
682int 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
737void GPTDataCurses::DrawMenu(void) {
738 string title="cgdisk ";
739 title += GPTFDISK_VERSION;
740 string drive="Disk Drive: ";
741 drive += device;
742 ostringstream size;
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400743
srs56943860cbe2011-09-10 20:29:53 -0400744 size << "Size: " << diskSize << ", " << BytesToIeee(diskSize, blockSize);
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400745
srs56943860cbe2011-09-10 20:29:53 -0400746 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
756int 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....
779void ClearLine(int lineNum) {
780 move(lineNum, 0);
781 clrtoeol();
782} // ClearLine()
783
784// Clear the last few lines of the display
785void ClearBottom(void) {
786 move(LINES - RESERVED_BOTTOM, 0);
787 clrtobot();
788} // ClearBottom()
789
790void 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.
799void 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.
812void ShowTypes(void) {
813 PartType tempType;
814 char junk;
815
816 def_prog_mode();
817 endwin();
Roderick W. Smithe3ee7332013-09-24 12:56:11 -0400818 tempType.ShowAllTypes(LINES - 3);
srs56943860cbe2011-09-10 20:29:53 -0400819 cout << "\nPress the <Enter> key to continue: ";
820 cin.get(junk);
821 reset_prog_mode();
822 refresh();
823} // ShowTypes()