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