~ubuntu-branches/ubuntu/utopic/gdisk/utopic-proposed

« back to all changes in this revision

Viewing changes to gptcurses.cc

  • Committer: Bazaar Package Importer
  • Author(s): Guillaume Delacour
  • Date: 2011-10-03 20:46:30 UTC
  • mfrom: (1.1.3 upstream)
  • Revision ID: james.westby@ubuntu.com-20111003204630-3cl1nosx48ofyig8
Tags: 0.8.1-1
* New upstream release
* debian/rules: Install cgdisk binary (curses based)
* debian/manpages: Install cgdisk manpage
* debian/control:
  + Remove Intel-based from description thanks Witold Baryluk
  (Closes: #642363)
  + Add libncurses5-dev in Build-Depends
* debian/copyright: Change to new DEP-5 format
* debian/patches/manpages.diff: Refresh patch to escape two hyphen

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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()