Upload Tizen:Base source
[framework/base/util-linux-ng.git] / fdisk / cfdisk.c
1 /****************************************************************************
2  *
3  *     CFDISK
4  *
5  * cfdisk is a curses based disk drive partitioning program that can
6  * create partitions for a wide variety of operating systems including
7  * Linux, MS-DOS and OS/2.
8  *
9  * cfdisk was inspired by the fdisk program, by A. V. Le Blanc
10  * (LeBlanc@mcc.ac.uk).
11  *
12  *     Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
13  *
14  * cfdisk is free software; you can redistribute it and/or modify it
15  * under the terms of the GNU General Public License as published by
16  * the Free Software Foundation; either version 2 of the License, or
17  * (at your option) any later version.
18  *
19  * cfdisk is distributed in the hope that it will be useful, but
20  * WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
22  * General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with cfdisk; if not, write to the Free Software Foundation,
26  * Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
27  *
28  * Created:     Fri Jan 28 22:46:58 1994, martin@cs.unc.edu
29  * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu
30  * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto
31  *                                           <jtklehto@stekt.oulu.fi>
32  * Versions 0.8e-p: aeb@cwi.nl
33  * Rebaptised 2.9p, following util-linux versioning.
34  *
35  *  Recognition of NTFS / HPFS difference inspired by patches
36  *  from Marty Leisner <leisner@sdsp.mc.xerox.com>
37  *  Exit codes by Enrique Zanardi <ezanardi@ull.es>:
38  *     0: all went well
39  *     1: command line error, out of memory
40  *     2: hardware problems [Cannot open/seek/read/write disk drive].
41  *     3: ioctl(fd, HDIO_GETGEO,...) failed. (Probably it is not a disk.)
42  *     4: bad partition table on disk. [Bad primary/logical partition].
43  *
44  * Sat, 23 Jan 1999 19:34:45 +0100 <Vincent.Renardias@ldsol.com>
45  *  Internationalized + provided initial French translation.
46  * Sat Mar 20 09:26:34 EST 1999 <acme@conectiva.com.br>
47  *  Some more i18n.
48  * Sun Jul 18 03:19:42 MEST 1999 <aeb@cwi.nl>
49  *  Terabyte-sized disks.
50  * Sat Jun 30 05:23:19 EST 2001 <nathans@sgi.com>
51  *  XFS label recognition.
52  * Thu Nov 22 15:42:56 CET 2001 <flavio.stanchina@tin.it>
53  *  ext3 and ReiserFS recognition.
54  * Sun Oct 12 17:43:43 CEST 2003 <flavio.stanchina@tin.it>
55  *  JFS recognition; ReiserFS label recognition.
56  *
57  ****************************************************************************/
58
59 #include <stdlib.h>
60 #include <stdio.h>
61 #include <stdarg.h>
62 #include <unistd.h>
63 #include <ctype.h>
64 #include <errno.h>
65 #include <getopt.h>
66 #include <fcntl.h>
67 #ifdef HAVE_SLCURSES_H
68 #include <slcurses.h>
69 #elif defined(HAVE_SLANG_SLCURSES_H)
70 #include <slang/slcurses.h>
71 #elif defined(HAVE_NCURSES_H)
72 #include <ncurses.h>
73 #elif defined(HAVE_NCURSES_NCURSES_H)
74 #include <ncurses/ncurses.h>
75 #endif
76 #include <signal.h>
77 #include <math.h>
78 #include <string.h>
79 #include <sys/stat.h>
80 #include <sys/ioctl.h>
81
82 #include "nls.h"
83 #include "blkdev.h"
84 #include "xstrncpy.h"
85 #include "common.h"
86 #include "gpt.h"
87
88 #ifdef __GNU__
89 #define DEFAULT_DEVICE "/dev/hd0"
90 #define ALTERNATE_DEVICE "/dev/sd0"
91 #elif defined(__FreeBSD__)
92 #define DEFAULT_DEVICE "/dev/ad0"
93 #define ALTERNATE_DEVICE "/dev/da0"
94 #else
95 #define DEFAULT_DEVICE "/dev/hda"
96 #define ALTERNATE_DEVICE "/dev/sda"
97 #endif
98
99 /* With K=1024 we have `binary' megabytes, gigabytes, etc.
100    Some misguided hackers like that.
101    With K=1000 we have MB and GB that follow the standards
102    [SI, ATA, IEEE etc] and the disk manufacturers and the law. */
103 #define K       1000
104
105 #define LINE_LENGTH 80
106 #define MAXIMUM_PARTS 60
107
108 #define SECTOR_SIZE 512
109
110 #define MAX_HEADS 256
111 #define MAX_SECTORS 63
112
113 #define ACTIVE_FLAG 0x80
114 #define PART_TABLE_FLAG0 0x55
115 #define PART_TABLE_FLAG1 0xAA
116
117 #define UNUSABLE -1
118 #define FREE_SPACE     0x00
119 #define DOS_EXTENDED   0x05
120 #define OS2_OR_NTFS    0x07
121 #define WIN98_EXTENDED 0x0f
122 #define LINUX_EXTENDED 0x85
123 #define LINUX_MINIX    0x81
124 #define LINUX_SWAP     0x82
125 #define LINUX          0x83
126
127 #define PRI_OR_LOG -1
128 #define PRIMARY -2
129 #define LOGICAL -3
130
131 #define COL_ID_WIDTH 25
132
133 #define CR '\015'
134 #define ESC '\033'
135 #define DEL '\177'
136 #define BELL '\007'
137 #define TAB '\011'
138 #define REDRAWKEY '\014'        /* ^L */
139 #define UPKEY '\020'            /* ^P */
140 #define DOWNKEY '\016'          /* ^N */
141
142 /* Display units */
143 #define GIGABYTES 1
144 #define MEGABYTES 2
145 #define SECTORS 3
146 #define CYLINDERS 4
147
148 #define GS_DEFAULT -1
149 #define GS_ESCAPE -2
150
151 #define PRINT_RAW_TABLE 1
152 #define PRINT_SECTOR_TABLE 2
153 #define PRINT_PARTITION_TABLE 4
154
155 #define IS_PRIMARY(p) ((p) >= 0 && (p) < 4)
156 #define IS_LOGICAL(p) ((p) > 3)
157
158 #define round_int(d) ((double)((int)(d+0.5)))
159 #define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d)))
160
161 struct partition {
162         unsigned char boot_ind;         /* 0x80 - active */
163         unsigned char head;             /* starting head */
164         unsigned char sector;           /* starting sector */
165         unsigned char cyl;              /* starting cylinder */
166         unsigned char sys_ind;          /* What partition type */
167         unsigned char end_head;         /* end head */
168         unsigned char end_sector;       /* end sector */
169         unsigned char end_cyl;          /* end cylinder */
170         unsigned char start4[4];        /* starting sector counting from 0 */
171         unsigned char size4[4];         /* nr of sectors in partition */
172 };
173
174 int heads = 0;
175 int sectors = 0;
176 long long cylinders = 0;
177 int cylinder_size = 0;          /* heads * sectors */
178 long long total_size = 0;       /* actual_size rounded down */
179 long long actual_size = 0;      /* (in 512-byte sectors) - set using ioctl */
180                                 /* explicitly given user values */
181 int user_heads = 0, user_sectors = 0;
182 long long user_cylinders = 0;
183                                 /* kernel values; ignore the cylinders */
184 int kern_heads = 0, kern_sectors = 0;
185                                 /* partition-table derived values */
186 int pt_heads = 0, pt_sectors = 0;
187
188
189 static void
190 set_hsc0(unsigned char *h, unsigned char *s, int *c, long long sector) {
191         *s = sector % sectors + 1;
192         sector /= sectors;
193         *h = sector % heads;
194         sector /= heads;
195         *c = sector;
196 }
197
198 static void
199 set_hsc(unsigned char *h, unsigned char *s, unsigned char *c,
200         long long sector) {
201         int cc;
202
203         if (sector >= 1024*cylinder_size)
204                 sector = 1024*cylinder_size - 1;
205         set_hsc0(h, s, &cc, sector);
206         *c = cc & 0xFF;
207         *s |= (cc >> 2) & 0xC0;
208 }
209
210 static void
211 set_hsc_begin(struct partition *p, long long sector) {
212         set_hsc(& p->head, & p->sector, & p->cyl, sector);
213 }
214
215 static void
216 set_hsc_end(struct partition *p, long long sector) {
217         set_hsc(& p->end_head, & p->end_sector, & p->end_cyl, sector);
218 }
219
220 #define is_extended(x)  ((x) == DOS_EXTENDED || (x) == WIN98_EXTENDED || \
221                          (x) == LINUX_EXTENDED)
222
223 #define is_dos_partition(x) ((x) == 1 || (x) == 4 || (x) == 6)
224 #define may_have_dos_label(x) (is_dos_partition(x) \
225    || (x) == 7 || (x) == 0xb || (x) == 0xc || (x) == 0xe \
226    || (x) == 0x11 || (x) == 0x14 || (x) == 0x16 || (x) == 0x17)
227
228 /* start_sect and nr_sects are stored little endian on all machines */
229 /* moreover, they are not aligned correctly */
230 static void
231 store4_little_endian(unsigned char *cp, unsigned int val) {
232         cp[0] = (val & 0xff);
233         cp[1] = ((val >> 8) & 0xff);
234         cp[2] = ((val >> 16) & 0xff);
235         cp[3] = ((val >> 24) & 0xff);
236 }
237
238 static unsigned int
239 read4_little_endian(unsigned char *cp) {
240         return (unsigned int)(cp[0]) + ((unsigned int)(cp[1]) << 8)
241                 + ((unsigned int)(cp[2]) << 16)
242                 + ((unsigned int)(cp[3]) << 24);
243 }
244
245 static void
246 set_start_sect(struct partition *p, unsigned int start_sect) {
247         store4_little_endian(p->start4, start_sect);
248 }
249
250 static unsigned int
251 get_start_sect(struct partition *p) {
252         return read4_little_endian(p->start4);
253 }
254
255 static void
256 set_nr_sects(struct partition *p, unsigned int nr_sects) {
257         store4_little_endian(p->size4, nr_sects);
258 }
259
260 static unsigned int
261 get_nr_sects(struct partition *p) {
262         return read4_little_endian(p->size4);
263 }
264
265 #define ALIGNMENT 2
266 typedef union {
267     struct {
268         unsigned char align[ALIGNMENT];
269         unsigned char b[SECTOR_SIZE];
270     } c;
271     struct {
272         unsigned char align[ALIGNMENT];
273         unsigned char buffer[0x1BE];
274         struct partition part[4];
275         unsigned char magicflag[2];
276     } p;
277 } partition_table;
278
279 typedef struct {
280     long long first_sector;     /* first sector in partition */
281     long long last_sector;      /* last sector in partition */
282     long offset;                /* offset from first sector to start of data */
283     int flags;          /* active == 0x80 */
284     int id;             /* filesystem type */
285     int num;            /* number of partition -- primary vs. logical */
286 #define LABELSZ 16
287     char volume_label[LABELSZ+1];
288 #define OSTYPESZ 8
289     char ostype[OSTYPESZ+1];
290 #define FSTYPESZ 8
291     char fstype[FSTYPESZ+1];
292 } partition_info;
293
294 char *disk_device = DEFAULT_DEVICE;
295 int fd;
296 int changed = FALSE;
297 int opened = FALSE;
298 int opentype;
299 int curses_started = 0;
300
301 partition_info p_info[MAXIMUM_PARTS];
302 partition_info ext_info;
303 int num_parts = 0;
304
305 int logical = 0;
306 long long logical_sectors[MAXIMUM_PARTS];
307
308 __sighandler_t old_SIGINT, old_SIGTERM;
309
310 int arrow_cursor = FALSE;
311 int display_units = MEGABYTES;
312 int zero_table = FALSE;
313 int use_partition_table_geometry = FALSE;
314 int print_only = 0;
315
316 /* Curses screen information */
317 int cur_part = 0;
318 int warning_last_time = FALSE;
319 int defined = FALSE;
320 int COLUMNS = 80;
321 int NUM_ON_SCREEN = 1;
322
323 /* Y coordinates */
324 int HEADER_START = 0;
325 int DISK_TABLE_START = 6;
326 int WARNING_START = 23;
327 int COMMAND_LINE_Y = 21;
328
329 /* X coordinates */
330 int NAME_START = 4;
331 int FLAGS_START = 16;
332 int PTYPE_START = 28;
333 int FSTYPE_START = 38;
334 int LABEL_START = 54;
335 int SIZE_START = 68;
336 int COMMAND_LINE_X = 5;
337
338 static void die_x(int ret);
339 static void draw_screen(void);
340
341 /* Guaranteed alloc */
342 static void *
343 xmalloc (size_t size) {
344      void *t;
345
346      if (size == 0)
347           return NULL;
348
349      t = malloc (size);
350      if (t == NULL) {
351           fprintf (stderr, _("%s: Out of memory!\n"), "cfdisk");
352           die_x(1);
353      }
354      return t;
355 }
356
357 /* Some libc's have their own basename() */
358 static char *
359 my_basename(char *devname) {
360     char *s = strrchr(devname, '/');
361     return s ? s+1 : devname;
362 }
363
364 static char *
365 partition_type_name(unsigned char type) {
366     struct systypes *s = i386_sys_types;
367
368     while(s->name && s->type != type)
369             s++;
370     return s->name;
371 }
372
373 static char *
374 partition_type_text(int i) {
375     if (p_info[i].id == UNUSABLE)
376          return _("Unusable");
377     else if (p_info[i].id == FREE_SPACE)
378          return _("Free Space");
379     else if (p_info[i].id == LINUX) {
380          if (!strcmp(p_info[i].fstype, "ext2"))
381               return _("Linux ext2");
382          else if (!strcmp(p_info[i].fstype, "ext3"))
383               return _("Linux ext3");
384          else if (!strcmp(p_info[i].fstype, "xfs"))
385               return _("Linux XFS");
386          else if (!strcmp(p_info[i].fstype, "jfs"))
387               return _("Linux JFS");
388          else if (!strcmp(p_info[i].fstype, "reiserfs"))
389               return _("Linux ReiserFS");
390          else
391               return _("Linux");
392     } else if (p_info[i].id == OS2_OR_NTFS) {
393          if (!strncmp(p_info[i].fstype, "HPFS", 4))
394               return _("OS/2 HPFS");
395          else if (!strncmp(p_info[i].ostype, "OS2", 3))
396               return _("OS/2 IFS");
397          else if (!p_info[i].ostype)
398               return p_info[i].ostype;
399          else
400               return _("NTFS");
401     } else
402          return _(partition_type_name(p_info[i].id));
403 }
404
405 static void
406 fdexit(int ret) {
407     if (opened)
408         close(fd);
409
410     if (changed) {
411         fprintf(stderr, _("Disk has been changed.\n"));
412 #if 0
413         fprintf(stderr, _("Reboot the system to ensure the partition "
414                         "table is correctly updated.\n"));
415 #endif
416
417         fprintf( stderr, _("\nWARNING: If you have created or modified any\n"
418                          "DOS 6.x partitions, please see the cfdisk manual\n"
419                          "page for additional information.\n") );
420     }
421
422     exit(ret);
423 }
424
425 static int
426 get_string(char *str, int len, char *def) {
427     unsigned char c;
428     int i = 0;
429     int x, y;
430     int use_def = FALSE;
431
432     getyx(stdscr, y, x);
433     clrtoeol();
434
435     str[i] = 0;
436
437     if (def != NULL) {
438         mvaddstr(y, x, def);
439         move(y, x);
440         use_def = TRUE;
441     }
442
443     refresh();
444     while ((c = getch()) != '\n' && c != CR) {
445         switch (c) {
446         case ESC:
447             move(y, x);
448             clrtoeol();
449             refresh();
450             return GS_ESCAPE;
451         case DEL:
452         case '\b':
453             if (i > 0) {
454                 str[--i] = 0;
455                 mvaddch(y, x+i, ' ');
456                 move(y, x+i);
457             } else if (use_def) {
458                 clrtoeol();
459                 use_def = FALSE;
460             } else
461                 putchar(BELL);
462             break;
463         default:
464             if (i < len && isprint(c)) {
465                 mvaddch(y, x+i, c);
466                 if (use_def) {
467                     clrtoeol();
468                     use_def = FALSE;
469                 }
470                 str[i++] = c;
471                 str[i] = 0;
472             } else
473                 putchar(BELL);
474         }
475         refresh();
476     }
477
478     if (use_def)
479         return GS_DEFAULT;
480     else
481         return i;
482 }
483
484 static void
485 clear_warning(void) {
486     int i;
487
488     if (!curses_started || !warning_last_time)
489         return;
490
491     move(WARNING_START,0);
492     for (i = 0; i < COLS; i++)
493         addch(' ');
494
495     warning_last_time = FALSE;
496 }
497
498 static void
499 print_warning(char *s) {
500     if (!curses_started) {
501          fprintf(stderr, "%s\n", s);
502     } else {
503         mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
504         putchar(BELL); /* CTRL-G */
505
506         warning_last_time = TRUE;
507     }
508 }
509
510 static void
511 fatal(char *s, int ret) {
512     char *err1 = _("FATAL ERROR");
513     char *err2 = _("Press any key to exit cfdisk");
514
515     if (curses_started) {
516          char *str = xmalloc(strlen(s) + strlen(err1) + strlen(err2) + 10);
517
518          sprintf(str, "%s: %s", err1, s);
519          if (strlen(str) > COLS)
520              str[COLS] = 0;
521          mvaddstr(WARNING_START, (COLS-strlen(str))/2, str);
522          sprintf(str, "%s", err2);
523          if (strlen(str) > COLS)
524              str[COLS] = 0;
525          mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str);
526          putchar(BELL); /* CTRL-G */
527          refresh();
528          (void)getch();
529          die_x(ret);
530     } else {
531          fprintf(stderr, "%s: %s\n", err1, s);
532          exit(ret);
533     }
534 }
535
536 static void
537 die(int dummy) {
538     die_x(0);
539 }
540
541 static void
542 die_x(int ret) {
543     signal(SIGINT, old_SIGINT);
544     signal(SIGTERM, old_SIGTERM);
545 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
546     SLsmg_gotorc(LINES-1, 0);
547     SLsmg_refresh();
548 #else
549     mvcur(0, COLS-1, LINES-1, 0);
550 #endif
551     nl();
552     endwin();
553     printf("\n");
554     fdexit(ret);
555 }
556
557 static void
558 read_sector(unsigned char *buffer, long long sect_num) {
559     if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
560         fatal(_("Cannot seek on disk drive"), 2);
561     if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
562         fatal(_("Cannot read disk drive"), 2);
563 }
564
565 static void
566 write_sector(unsigned char *buffer, long long sect_num) {
567     if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
568         fatal(_("Cannot seek on disk drive"), 2);
569     if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
570         fatal(_("Cannot write disk drive"), 2);
571 }
572
573 static void
574 dos_copy_to_info(char *to, int tosz, char *from, int fromsz) {
575      int i;
576
577      for(i=0; i<tosz && i<fromsz && isascii(from[i]); i++)
578           to[i] = from[i];
579      to[i] = 0;
580 }
581
582 static void
583 get_dos_label(int i) {
584         char sector[128];
585 #define DOS_OSTYPE_OFFSET 3
586 #define DOS_LABEL_OFFSET 43
587 #define DOS_FSTYPE_OFFSET 54
588 #define DOS_OSTYPE_SZ 8
589 #define DOS_LABEL_SZ 11
590 #define DOS_FSTYPE_SZ 8
591         long long offset;
592
593         offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE;
594         if (lseek(fd, offset, SEEK_SET) == offset
595             && read(fd, &sector, sizeof(sector)) == sizeof(sector)) {
596                 dos_copy_to_info(p_info[i].ostype, OSTYPESZ,
597                                  sector+DOS_OSTYPE_OFFSET, DOS_OSTYPE_SZ);
598                 dos_copy_to_info(p_info[i].volume_label, LABELSZ,
599                                  sector+DOS_LABEL_OFFSET, DOS_LABEL_SZ);
600                 dos_copy_to_info(p_info[i].fstype, FSTYPESZ,
601                                  sector+DOS_FSTYPE_OFFSET, DOS_FSTYPE_SZ);
602         }
603 }
604
605 #define REISERFS_SUPER_MAGIC_STRING "ReIsErFs"
606 #define REISER2FS_SUPER_MAGIC_STRING "ReIsEr2Fs"
607 struct reiserfs_super_block {
608         char s_dummy0[52];
609         char s_magic [10];
610         char s_dummy1[38];
611         u_char s_label[16];
612 };
613 #define REISERFSLABELSZ sizeof(reiserfsb.s_label)
614
615 static int
616 has_reiserfs_magic_string(const struct reiserfs_super_block *rs, int *is_3_6) {
617         if (!strncmp(rs->s_magic, REISERFS_SUPER_MAGIC_STRING,
618                      strlen(REISERFS_SUPER_MAGIC_STRING))) {
619                 *is_3_6 = 0;
620                 return 1;
621         }
622         if (!strncmp(rs->s_magic, REISER2FS_SUPER_MAGIC_STRING,
623                      strlen(REISER2FS_SUPER_MAGIC_STRING))) {
624                 *is_3_6 = 1;
625                 return 1;
626         }
627         return 0;
628 }
629
630 static void
631 get_linux_label(int i) {
632
633 #define EXT2LABELSZ 16
634 #define EXT2_SUPER_MAGIC 0xEF53
635 #define EXT3_FEATURE_COMPAT_HAS_JOURNAL 0x0004
636         struct ext2_super_block {
637                 char  s_dummy0[56];
638                 unsigned char  s_magic[2];
639                 char  s_dummy1[34];
640                 unsigned char  s_feature_compat[4];
641                 char  s_dummy2[24];
642                 char  s_volume_name[EXT2LABELSZ];
643                 char  s_last_mounted[64];
644                 char  s_dummy3[824];
645         } e2fsb;
646
647 #define REISERFS_DISK_OFFSET_IN_BYTES (64 * 1024)
648         struct reiserfs_super_block reiserfsb;
649         int reiserfs_is_3_6;
650
651 #define JFS_SUPER1_OFF  0x8000
652 #define JFS_MAGIC       "JFS1"
653 #define JFSLABELSZ      16
654         struct jfs_super_block {
655                 char    s_magic[4];
656                 u_char  s_version[4];
657                 u_char  s_dummy1[93];
658                 char    s_fpack[11];
659                 u_char  s_dummy2[24];
660                 u_char  s_uuid[16];
661                 char    s_label[JFSLABELSZ];
662         } jfsb;
663
664 #define XFS_SUPER_MAGIC "XFSB"
665 #define XFSLABELSZ 12
666         struct xfs_super_block {
667                 unsigned char   s_magic[4];
668                 unsigned char   s_dummy0[104];
669                 unsigned char   s_fname[XFSLABELSZ];
670                 unsigned char   s_dummy1[904];
671         } xfsb;
672
673         char *label;
674         long long offset;
675         int j;
676
677         offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE
678                 + 1024;
679         if (lseek(fd, offset, SEEK_SET) == offset
680             && read(fd, &e2fsb, sizeof(e2fsb)) == sizeof(e2fsb)
681             && e2fsb.s_magic[0] + (e2fsb.s_magic[1]<<8) == EXT2_SUPER_MAGIC) {
682                 label = e2fsb.s_volume_name;
683                 for(j=0; j<EXT2LABELSZ && j<LABELSZ && isprint(label[j]); j++)
684                         p_info[i].volume_label[j] = label[j];
685                 p_info[i].volume_label[j] = 0;
686                 /* ext2 or ext3? */
687                 if (e2fsb.s_feature_compat[0]&EXT3_FEATURE_COMPAT_HAS_JOURNAL)
688                         strncpy(p_info[i].fstype, "ext3", FSTYPESZ);
689                 else
690                         strncpy(p_info[i].fstype, "ext2", FSTYPESZ);
691                 return;
692         }
693
694         offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE + 0;
695         if (lseek(fd, offset, SEEK_SET) == offset
696             && read(fd, &xfsb, sizeof(xfsb)) == sizeof(xfsb)
697             && !strncmp((char *) xfsb.s_magic, XFS_SUPER_MAGIC, 4)) {
698                 label = (char *) xfsb.s_fname;
699                 for(j=0; j<XFSLABELSZ && j<LABELSZ && isprint(label[j]); j++)
700                         p_info[i].volume_label[j] = label[j];
701                 p_info[i].volume_label[j] = 0;
702                 strncpy(p_info[i].fstype, "xfs", FSTYPESZ);
703                 return;
704         }
705
706         /* jfs? */
707         offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE
708                 + JFS_SUPER1_OFF;
709         if (lseek(fd, offset, SEEK_SET) == offset
710             && read(fd, &jfsb, sizeof(jfsb)) == sizeof(jfsb)
711             && !strncmp(jfsb.s_magic, JFS_MAGIC, strlen(JFS_MAGIC))) {
712                 label = jfsb.s_label;
713                 for(j=0; j<JFSLABELSZ && j<LABELSZ && isprint(label[j]); j++)
714                         p_info[i].volume_label[j] = label[j];
715                 p_info[i].volume_label[j] = 0;
716                 strncpy(p_info[i].fstype, "jfs", FSTYPESZ);
717                 return;
718         }
719
720         /* reiserfs? */
721         offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE
722                 + REISERFS_DISK_OFFSET_IN_BYTES;
723         if (lseek(fd, offset, SEEK_SET) == offset
724             && read(fd, &reiserfsb, sizeof(reiserfsb)) == sizeof(reiserfsb)
725             && has_reiserfs_magic_string(&reiserfsb, &reiserfs_is_3_6)) {
726                 if (reiserfs_is_3_6) {
727                         /* label only on version 3.6 onward */
728                         label = (char *) reiserfsb.s_label;
729                         for(j=0; j<REISERFSLABELSZ && j<LABELSZ &&
730                                     isprint(label[j]); j++)
731                                 p_info[i].volume_label[j] = label[j];
732                         p_info[i].volume_label[j] = 0;
733                 }
734                 strncpy(p_info[i].fstype, "reiserfs", FSTYPESZ);
735                 return;
736         }
737 }
738
739 static void
740 check_part_info(void) {
741     int i, pri = 0, log = 0;
742
743     for (i = 0; i < num_parts; i++)
744         if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
745             pri++;
746         else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
747             log++;
748     if (is_extended(ext_info.id)) {
749         if (log > 0)
750             pri++;
751         else {
752             ext_info.first_sector = 0;
753             ext_info.last_sector = 0;
754             ext_info.offset = 0;
755             ext_info.flags = 0;
756             ext_info.id = FREE_SPACE;
757             ext_info.num = PRIMARY;
758         }
759     }
760
761     if (pri >= 4) {
762         for (i = 0; i < num_parts; i++)
763             if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE) {
764                 if (is_extended(ext_info.id)) {
765                     if (p_info[i].first_sector >= ext_info.first_sector &&
766                         p_info[i].last_sector <= ext_info.last_sector) {
767                         p_info[i].id = FREE_SPACE;
768                         p_info[i].num = LOGICAL;
769                     } else if (i > 0 &&
770                                p_info[i-1].first_sector >=
771                                ext_info.first_sector &&
772                                p_info[i-1].last_sector <=
773                                ext_info.last_sector) {
774                         p_info[i].id = FREE_SPACE;
775                         p_info[i].num = LOGICAL;
776                     } else if (i < num_parts-1 &&
777                                p_info[i+1].first_sector >=
778                                ext_info.first_sector &&
779                                p_info[i+1].last_sector <=
780                                ext_info.last_sector) {
781                         p_info[i].id = FREE_SPACE;
782                         p_info[i].num = LOGICAL;
783                     } else
784                         p_info[i].id = UNUSABLE;
785                 } else /* if (!is_extended(ext_info.id)) */
786                     p_info[i].id = UNUSABLE;
787             } else /* if (p_info[i].id > 0) */
788                 while (0); /* Leave these alone */
789     } else { /* if (pri < 4) */
790         for (i = 0; i < num_parts; i++) {
791             if (p_info[i].id == UNUSABLE)
792                 p_info[i].id = FREE_SPACE;
793             if (p_info[i].id == FREE_SPACE) {
794                 if (is_extended(ext_info.id)) {
795                     if (p_info[i].first_sector >= ext_info.first_sector &&
796                         p_info[i].last_sector <= ext_info.last_sector)
797                         p_info[i].num = LOGICAL;
798                     else if (i > 0 &&
799                              p_info[i-1].first_sector >=
800                              ext_info.first_sector &&
801                              p_info[i-1].last_sector <=
802                              ext_info.last_sector)
803                         p_info[i].num = PRI_OR_LOG;
804                     else if (i < num_parts-1 &&
805                              p_info[i+1].first_sector >=
806                              ext_info.first_sector &&
807                              p_info[i+1].last_sector <=
808                              ext_info.last_sector)
809                         p_info[i].num = PRI_OR_LOG;
810                     else
811                         p_info[i].num = PRIMARY;
812                 } else /* if (!is_extended(ext_info.id)) */
813                     p_info[i].num = PRI_OR_LOG;
814             } else /* if (p_info[i].id > 0) */
815                 while (0); /* Leave these alone */
816         }
817     }
818 }
819
820 static void
821 remove_part(int i) {
822     int p;
823
824     for (p = i; p < num_parts; p++)
825         p_info[p] = p_info[p+1];
826
827     num_parts--;
828     if (cur_part == num_parts)
829         cur_part--;
830 }
831
832 static void
833 insert_empty_part(int i, long long first, long long last) {
834     int p;
835
836     for (p = num_parts; p > i; p--)
837          p_info[p] = p_info[p-1];
838
839     p_info[i].first_sector = first;
840     p_info[i].last_sector = last;
841     p_info[i].offset = 0;
842     p_info[i].flags = 0;
843     p_info[i].id = FREE_SPACE;
844     p_info[i].num = PRI_OR_LOG;
845     p_info[i].volume_label[0] = 0;
846     p_info[i].fstype[0] = 0;
847     p_info[i].ostype[0] = 0;
848
849     num_parts++;
850 }
851
852 static void
853 del_part(int i) {
854     int num = p_info[i].num;
855
856     if (i > 0 && (p_info[i-1].id == FREE_SPACE ||
857                   p_info[i-1].id == UNUSABLE)) {
858         /* Merge with previous partition */
859         p_info[i-1].last_sector = p_info[i].last_sector;
860         remove_part(i--);
861     }
862
863     if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE ||
864                               p_info[i+1].id == UNUSABLE)) {
865         /* Merge with next partition */
866         p_info[i+1].first_sector = p_info[i].first_sector;
867         remove_part(i);
868     }
869
870     if (i > 0)
871         p_info[i].first_sector = p_info[i-1].last_sector + 1;
872     else
873         p_info[i].first_sector = 0;
874
875     if (i < num_parts - 1)
876         p_info[i].last_sector = p_info[i+1].first_sector - 1;
877     else
878         p_info[i].last_sector = total_size - 1;
879
880     p_info[i].offset = 0;
881     p_info[i].flags = 0;
882     p_info[i].id = FREE_SPACE;
883     p_info[i].num = PRI_OR_LOG;
884
885     if (IS_LOGICAL(num)) {
886         /* We have a logical partition --> shrink the extended partition
887          * if (1) this is the first logical drive, or (2) this is the
888          * last logical drive; and if there are any other logical drives
889          * then renumber the ones after "num".
890          */
891         if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num))) {
892             ext_info.first_sector = p_info[i].last_sector + 1;
893             ext_info.offset = 0;
894         }
895         if (i == num_parts-1 ||
896             (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)))
897             ext_info.last_sector = p_info[i].first_sector - 1;
898         for (i = 0; i < num_parts; i++)
899             if (p_info[i].num > num)
900                 p_info[i].num--;
901     }
902
903     /* Clean up the rest of the partitions */
904     check_part_info();
905 }
906
907 static int
908 add_part(int num, int id, int flags, long long first, long long last,
909          long long offset, int want_label, char **errmsg) {
910     int i, pri = 0, log = 0;
911
912     if (num_parts == MAXIMUM_PARTS) {
913         *errmsg = _("Too many partitions");
914         return -1;
915     }
916
917     if (first < 0) {
918         *errmsg = _("Partition begins before sector 0");
919         return -1;
920     }
921
922     if (last < 0) {
923         *errmsg = _("Partition ends before sector 0");
924         return -1;
925     }
926
927     if (first >= total_size) {
928         *errmsg = _("Partition begins after end-of-disk");
929         return -1;
930     }
931
932     if (last >= actual_size) {
933         *errmsg = _("Partition ends after end-of-disk");
934         return -1;
935     }
936
937     if (last >= total_size) {
938         *errmsg = _("Partition ends in the final partial cylinder");
939         return -1;
940     }
941
942     for (i = 0; i < num_parts; i++) {
943         if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
944             pri++;
945         else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
946             log++;
947     }
948     if (is_extended(ext_info.id) && log > 0)
949         pri++;
950
951     if (IS_PRIMARY(num)) {
952         if (pri >= 4) {
953             return -1;          /* no room for more */
954         } else
955             pri++;
956     }
957
958     for (i = 0; i < num_parts && p_info[i].last_sector < first; i++);
959
960     if (i < num_parts && p_info[i].id != FREE_SPACE) {
961          if (last < p_info[i].first_sector)
962               *errmsg = _("logical partitions not in disk order");
963          else if (first + offset <= p_info[i].last_sector &&
964                   p_info[i].first_sector + p_info[i].offset <= last)
965               *errmsg = _("logical partitions overlap");
966          else
967               /* the enlarged logical partition starts at the
968                  partition table sector that defines it */
969               *errmsg = _("enlarged logical partitions overlap");
970          return -1;
971     }
972
973     if (i == num_parts || last > p_info[i].last_sector) {
974         return -1;
975     }
976
977     if (is_extended(id)) {
978         if (ext_info.id != FREE_SPACE) {
979             return -1;          /* second extended */
980         }
981         else if (IS_PRIMARY(num)) {
982             ext_info.first_sector = first;
983             ext_info.last_sector = last;
984             ext_info.offset = offset;
985             ext_info.flags = flags;
986             ext_info.id = id;
987             ext_info.num = num;
988             ext_info.volume_label[0] = 0;
989             ext_info.fstype[0] = 0;
990             ext_info.ostype[0] = 0;
991             return 0;
992         } else {
993             return -1;          /* explicit extended logical */
994         }
995     }
996
997     if (IS_LOGICAL(num)) {
998         if (!is_extended(ext_info.id)) {
999             print_warning(_("!!!! Internal error creating logical "
1000                           "drive with no extended partition !!!!"));
1001         } else {
1002             /* We might have a logical partition outside of the extended
1003              * partition's range --> we have to extend the extended
1004              * partition's range to encompass this new partition, but we
1005              * must make sure that there are no primary partitions between
1006              * it and the closest logical drive in extended partition.
1007              */
1008             if (first < ext_info.first_sector) {
1009                 if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) {
1010                     print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
1011                     return -1;
1012                 } else {
1013                     if (first == 0) {
1014                         ext_info.first_sector = 0;
1015                         ext_info.offset = first = offset;
1016                     } else {
1017                         ext_info.first_sector = first;
1018                     }
1019                 }
1020             } else if (last > ext_info.last_sector) {
1021                 if (i > 0 && IS_PRIMARY(p_info[i-1].num)) {
1022                     print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
1023                     return -1;
1024                 } else {
1025                     ext_info.last_sector = last;
1026                 }
1027             }
1028         }
1029     }
1030
1031     if (first != p_info[i].first_sector &&
1032         !(IS_LOGICAL(num) && first == offset)) {
1033         insert_empty_part(i, p_info[i].first_sector, first-1);
1034         i++;
1035     }
1036
1037     if (last != p_info[i].last_sector)
1038         insert_empty_part(i+1, last+1, p_info[i].last_sector);
1039
1040     p_info[i].first_sector = first;
1041     p_info[i].last_sector = last;
1042     p_info[i].offset = offset;
1043     p_info[i].flags = flags;
1044     p_info[i].id = id;
1045     p_info[i].num = num;
1046     p_info[i].volume_label[0] = 0;
1047     p_info[i].fstype[0] = 0;
1048     p_info[i].ostype[0] = 0;
1049     if (want_label) {
1050          if (may_have_dos_label(id))
1051               get_dos_label(i);
1052          else if (id == LINUX)
1053               get_linux_label(i);
1054     }
1055
1056     check_part_info();
1057
1058     return 0;
1059 }
1060
1061 static int
1062 find_primary(void) {
1063     int num = 0, cur = 0;
1064
1065     while (cur < num_parts && IS_PRIMARY(num))
1066         if ((p_info[cur].id > 0 && p_info[cur].num == num) ||
1067             (is_extended(ext_info.id) && ext_info.num == num)) {
1068             num++;
1069             cur = 0;
1070         } else
1071             cur++;
1072
1073     if (!IS_PRIMARY(num))
1074         return -1;
1075     else
1076         return num;
1077 }
1078
1079 static int
1080 find_logical(int i) {
1081     int num = -1;
1082     int j;
1083
1084     for (j = i; j < num_parts && num == -1; j++)
1085         if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
1086             num = p_info[j].num;
1087
1088     if (num == -1) {
1089         num = 4;
1090         for (j = 0; j < num_parts; j++)
1091             if (p_info[j].id > 0 && p_info[j].num == num)
1092                 num++;
1093     }
1094
1095     return num;
1096 }
1097
1098 /*
1099  * Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi>
1100  * September 1994
1101  */
1102
1103 /* Constants for menuType parameter of menuSelect function */
1104 #define MENU_HORIZ 1
1105 #define MENU_VERT 2
1106 #define MENU_ACCEPT_OTHERS 4
1107 #define MENU_BUTTON 8
1108 /* Miscellenous constants */
1109 #define MENU_SPACING 2
1110 #define MENU_MAX_ITEMS 256 /* for simpleMenu function */
1111 #define MENU_UP 1
1112 #define MENU_DOWN 2
1113 #define MENU_RIGHT 3
1114 #define MENU_LEFT 4
1115
1116 struct MenuItem
1117 {
1118     int key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */
1119     char *name; /* Item name, should be eight characters with current implementation */
1120     char *desc; /* Item description to be printed when item is selected */
1121 };
1122
1123 /*
1124  * Actual function which prints the button bar and highlights the active button
1125  * Should not be called directly. Call function menuSelect instead.
1126  */
1127
1128 static int
1129 menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength,
1130             char *available, int menuType, int current ) {
1131     int i, lmargin = x, ymargin = y;
1132     char *mcd;
1133
1134     /* Print available buttons */
1135     move( y, x ); clrtoeol();
1136
1137     for( i = 0; menuItems[i].key; i++ ) {
1138         char buff[20];
1139         int lenName;
1140         const char *mi;
1141
1142         /* Search next available button */
1143         while( menuItems[i].key && !strchr(available, menuItems[i].key) )
1144             i++;
1145
1146         if( !menuItems[i].key ) break; /* No more menu items */
1147
1148         /* If selected item is not available and we have bypassed it,
1149            make current item selected */
1150         if( current < i && menuItems[current].key < 0 ) current = i;
1151
1152         /* If current item is selected, highlight it */
1153         if( current == i ) /*attron( A_REVERSE )*/ standout ();
1154
1155         /* Print item */
1156         /* Because of a bug in gettext() we must not translate empty strings */
1157         if (menuItems[i].name[0])
1158                 mi = _(menuItems[i].name);
1159         else
1160                 mi = "";
1161         lenName = strlen( mi );
1162 #if 0
1163         if(lenName > itemLength || lenName >= sizeof(buff))
1164             print_warning(_("Menu item too long. Menu may look odd."));
1165 #endif
1166         if (lenName >= sizeof(buff)) {  /* truncate ridiculously long string */
1167             xstrncpy(buff, mi, sizeof(buff));
1168         } else if (lenName >= itemLength) {
1169             snprintf(buff, sizeof(buff),
1170                      (menuType & MENU_BUTTON) ? "[%s]" : "%s", mi);
1171         } else {
1172             snprintf(buff, sizeof(buff),
1173                      (menuType & MENU_BUTTON) ? "[%*s%-*s]" : "%*s%-*s",
1174                      (itemLength - lenName) / 2, "",
1175                      (itemLength - lenName + 1) / 2 + lenName, mi);
1176         }
1177         mvaddstr( y, x, buff );
1178
1179         /* Lowlight after selected item */
1180         if( current == i ) /*attroff( A_REVERSE )*/ standend ();
1181
1182         /* Calculate position for the next item */
1183         if( menuType & MENU_VERT )
1184         {
1185             y += 1;
1186             if( y >= WARNING_START )
1187             {
1188                 y = ymargin;
1189                 x += itemLength + MENU_SPACING;
1190                 if( menuType & MENU_BUTTON ) x += 2;
1191             }
1192         }
1193         else
1194         {
1195             x += itemLength + MENU_SPACING;
1196             if( menuType & MENU_BUTTON ) x += 2;
1197             if( x > COLUMNS - lmargin - 12 )
1198             {
1199                 x = lmargin;
1200                 y ++ ;
1201             }
1202         }
1203     }
1204
1205     /* Print the description of selected item */
1206     mcd = _(menuItems[current].desc);
1207     mvaddstr( WARNING_START + 1, (COLUMNS - strlen( mcd )) / 2, mcd );
1208     return y;
1209 }
1210
1211 /* This function takes a list of menu items, lets the user choose one *
1212  * and returns the keyboard shortcut value of the selected menu item  */
1213
1214 static int
1215 menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength,
1216             char *available, int menuType, int menuDefault ) {
1217     int i, ylast = y, key = 0, current = menuDefault;
1218
1219     if( !( menuType & ( MENU_HORIZ | MENU_VERT ) ) ) {
1220         print_warning(_("Menu without direction. Defaulting to horizontal."));
1221         menuType |= MENU_HORIZ;
1222     }
1223
1224     /* Make sure that the current is one of the available items */
1225     while( !strchr(available, menuItems[current].key) ) {
1226         current ++ ;
1227         if( !menuItems[current].key ) current = 0;
1228     }
1229
1230     /* Repeat until allowable choice has been made */
1231     while( !key ) {
1232         /* Display the menu and read a command */
1233         ylast = menuUpdate( y, x, menuItems, itemLength, available,
1234                             menuType, current );
1235         refresh();
1236         key = getch();
1237
1238         /* Clear out all prompts and such */
1239         clear_warning();
1240         for (i = y; i < ylast; i++) {
1241             move(i, x);
1242             clrtoeol();
1243         }
1244         move( WARNING_START + 1, 0 );
1245         clrtoeol();
1246
1247         /* Cursor keys - possibly split by slow connection */
1248         if( key == ESC ) {
1249             /* Check whether this is a real ESC or one of extended keys */
1250             /*nodelay(stdscr, TRUE);*/
1251             key = getch();
1252             /*nodelay(stdscr, FALSE);*/
1253
1254             if( key == /*ERR*/ ESC ) {
1255                 /* This is a real ESC */
1256                 key = ESC;
1257             }
1258             if(key == '[' || key == 'O') {
1259                 /* This is one extended keys */
1260                 key = getch();
1261
1262                 switch(key) {
1263                     case 'A': /* Up arrow */
1264                         key = MENU_UP;
1265                         break;
1266                     case 'B': /* Down arrow */
1267                         key = MENU_DOWN;
1268                         break;
1269                     case 'C': /* Right arrow */
1270                         key = MENU_RIGHT;
1271                         break;
1272                     case 'D': /* Left arrow */
1273                         key = MENU_LEFT;
1274                         break;
1275                     default:
1276                         key = 0;
1277                 }
1278             }
1279         }
1280
1281         /* Enter equals the keyboard shortcut of current menu item */
1282         if (key == CR)
1283             key = menuItems[current].key;
1284
1285         /* Give alternatives for arrow keys in case the window manager
1286            swallows these */
1287         if (key == TAB)
1288             key = MENU_RIGHT;
1289         if (key == UPKEY)       /* ^P */
1290             key = MENU_UP;
1291         if (key == DOWNKEY)     /* ^N */
1292             key = MENU_DOWN;
1293
1294         if (key == MENU_UP) {
1295             if( menuType & MENU_VERT ) {
1296                 do {
1297                     current -- ;
1298                     if( current < 0 )
1299                         while( menuItems[current+1].key )
1300                             current ++ ;
1301                 } while( !strchr( available, menuItems[current].key ));
1302                 key = 0;
1303             }
1304         }
1305
1306         if (key == MENU_DOWN) {
1307             if( menuType & MENU_VERT ) {
1308                 do {
1309                     current ++ ;
1310                     if( !menuItems[current].key ) current = 0 ;
1311                 } while( !strchr( available, menuItems[current].key ));
1312                 key = 0;
1313             }
1314         }
1315
1316         if (key == MENU_RIGHT) {
1317             if( menuType & MENU_HORIZ ) {
1318                 do {
1319                     current ++ ;
1320                     if( !menuItems[current].key )
1321                         current = 0 ;
1322                 } while( !strchr( available, menuItems[current].key ));
1323                 key = 0;
1324             }
1325         }
1326
1327         if (key == MENU_LEFT) {
1328              if( menuType & MENU_HORIZ ) {
1329                  do {
1330                      current -- ;
1331                      if( current < 0 ) {
1332                          while( menuItems[current + 1].key )
1333                               current ++ ;
1334                      }
1335                  } while( !strchr( available, menuItems[current].key ));
1336                  key = 0;
1337              }
1338         }
1339
1340         /* Should all keys to be accepted? */
1341         if( key && (menuType & MENU_ACCEPT_OTHERS) ) break;
1342
1343         /* Is pressed key among acceptable ones? */
1344         if( key && (strchr(available, tolower(key)) || strchr(available, key)))
1345             break;
1346
1347         /* The key has not been accepted so far -> let's reject it */
1348         if (key) {
1349             key = 0;
1350             putchar( BELL );
1351             print_warning(_("Illegal key"));
1352         }
1353     }
1354
1355     /* Clear out prompts and such */
1356     clear_warning();
1357     for( i = y; i <= ylast; i ++ ) {
1358         move( i, x );
1359         clrtoeol();
1360     }
1361     move( WARNING_START + 1, 0 );
1362     clrtoeol();
1363     return key;
1364 }
1365
1366 /* A function which displays "Press a key to continue"                  *
1367  * and waits for a keypress.                                            *
1368  * Perhaps calling function menuSelect is a bit overkill but who cares? */
1369
1370 static void
1371 menuContinue(void) {
1372     static struct MenuItem menuContinueBtn[]=
1373     {
1374         { 'c', "", N_("Press a key to continue") },
1375         { 0, NULL, NULL }
1376     };
1377
1378     menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X,
1379         menuContinueBtn, 0, "c", MENU_HORIZ | MENU_ACCEPT_OTHERS, 0 );
1380 }
1381
1382 /* Function menuSelect takes way too many parameters  *
1383  * Luckily, most of time we can do with this function */
1384
1385 static int
1386 menuSimple(struct MenuItem *menuItems, int menuDefault) {
1387     int i, j, itemLength = 0;
1388     char available[MENU_MAX_ITEMS];
1389
1390     for(i = 0; menuItems[i].key; i++)
1391     {
1392         j = strlen( _(menuItems[i].name) );
1393         if( j > itemLength ) itemLength = j;
1394         available[i] = menuItems[i].key;
1395     }
1396     available[i] = 0;
1397     return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength,
1398         available, MENU_HORIZ | MENU_BUTTON, menuDefault);
1399 }
1400
1401 /* End of command menu support code */
1402
1403 static void
1404 new_part(int i) {
1405     char response[LINE_LENGTH], def[LINE_LENGTH];
1406     char c;
1407     long long first = p_info[i].first_sector;
1408     long long last = p_info[i].last_sector;
1409     long long offset = 0;
1410     int flags = 0;
1411     int id = LINUX;
1412     int num = -1;
1413     long long num_sects = last - first + 1;
1414     int len, ext, j;
1415     char *errmsg;
1416     double sectors_per_MB = K*K / 512.0;
1417
1418     if (p_info[i].num == PRI_OR_LOG) {
1419         static struct MenuItem menuPartType[]=
1420         {
1421             { 'p', N_("Primary"), N_("Create a new primary partition") },
1422             { 'l', N_("Logical"), N_("Create a new logical partition") },
1423             { ESC, N_("Cancel"), N_("Don't create a partition") },
1424             { 0, NULL, NULL }
1425         };
1426
1427         c = menuSimple( menuPartType, 0 );
1428         if (toupper(c) == 'P')
1429             num = find_primary();
1430         else if (toupper(c) == 'L')
1431             num = find_logical(i);
1432         else
1433             return;
1434     } else if (p_info[i].num == PRIMARY)
1435         num = find_primary();
1436     else if (p_info[i].num == LOGICAL)
1437         num = find_logical(i);
1438     else
1439         print_warning(_("!!! Internal error !!!"));
1440
1441     snprintf(def, sizeof(def), "%.2f", num_sects/sectors_per_MB);
1442     mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Size (in MB): "));
1443     if ((len = get_string(response, LINE_LENGTH, def)) <= 0 &&
1444         len != GS_DEFAULT)
1445         return;
1446     else if (len > 0) {
1447 #define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/cylinder_size))
1448         for (j = 0;
1449              j < len-1 && (isdigit(response[j]) || response[j] == '.');
1450              j++);
1451         if (toupper(response[j]) == 'K') {
1452             num_sects = num_cyls(atof(response)*K)*cylinder_size;
1453         } else if (toupper(response[j]) == 'M') {
1454             num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
1455         } else if (toupper(response[j]) == 'G') {
1456             num_sects = num_cyls(atof(response)*K*K*K)*cylinder_size;
1457         } else if (toupper(response[j]) == 'C') {
1458             num_sects = round_int(atof(response))*cylinder_size;
1459         } else if (toupper(response[j]) == 'S') {
1460             num_sects = round_int(atof(response));
1461         } else {
1462             num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
1463         }
1464     }
1465
1466     if (num_sects <= 0 ||
1467         num_sects > p_info[i].last_sector - p_info[i].first_sector + 1)
1468         return;
1469
1470     move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol();
1471     if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) {
1472         /* Determine where inside free space to put partition.
1473          */
1474         static struct MenuItem menuPlace[]=
1475         {
1476             { 'b', N_("Beginning"), N_("Add partition at beginning of free space") },
1477             { 'e', N_("End"), N_("Add partition at end of free space") },
1478             { ESC, N_("Cancel"), N_("Don't create a partition") },
1479             { 0, NULL, NULL }
1480         };
1481         c = menuSimple( menuPlace, 0 );
1482         if (toupper(c) == 'B')
1483             last = first + num_sects - 1;
1484         else if (toupper(c) == 'E')
1485             first = last - num_sects + 1;
1486         else
1487             return;
1488     }
1489
1490     if (IS_LOGICAL(num) && !is_extended(ext_info.id)) {
1491         /* We want to add a logical partition, but need to create an
1492          * extended partition first.
1493          */
1494         if ((ext = find_primary()) < 0) {
1495             print_warning(_("No room to create the extended partition"));
1496             return;
1497         }
1498         errmsg = 0;
1499         if (add_part(ext, DOS_EXTENDED, 0, first, last,
1500                      (first == 0 ? sectors : 0), 0, &errmsg) && errmsg)
1501                 print_warning(errmsg);
1502         first = ext_info.first_sector + ext_info.offset;
1503     }
1504
1505     /* increment number of all partitions past this one */
1506     if (IS_LOGICAL(num)) {
1507 #if 0
1508         /* original text - ok, but fails when partitions not in disk order */
1509         for (j = i; j < num_parts; j++)
1510             if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
1511                 p_info[j].num++;
1512 #else
1513         /* always ok */
1514         for (j = 0; j < num_parts; j++)
1515             if (p_info[j].id > 0 && p_info[j].num >= num)
1516                 p_info[j].num++;
1517 #endif
1518     }
1519
1520     /* Now we have a complete partition to ourselves */
1521     if (first == 0 || IS_LOGICAL(num))
1522         offset = sectors;
1523
1524     errmsg = 0;
1525     if (add_part(num, id, flags, first, last, offset, 0, &errmsg) && errmsg)
1526             print_warning(errmsg);
1527 }
1528
1529 static void
1530 get_kernel_geometry(void) {
1531 #ifdef HDIO_GETGEO
1532     struct hd_geometry geometry;
1533
1534     if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
1535         kern_heads = geometry.heads;
1536         kern_sectors = geometry.sectors;
1537     }
1538 #endif
1539 }
1540
1541 static int
1542 said_yes(char answer) {
1543 #ifdef HAVE_RPMATCH
1544         char reply[2];
1545         int yn;
1546
1547         reply[0] = answer;
1548         reply[1] = 0;
1549         yn = rpmatch(reply);    /* 1: yes, 0: no, -1: ? */
1550         if (yn >= 0)
1551                 return yn;
1552 #endif
1553         return (answer == 'y' || answer == 'Y');
1554 }
1555
1556 static void
1557 get_partition_table_geometry(partition_table *bufp) {
1558         struct partition *p;
1559         int i,h,s,hh,ss;
1560         int first = TRUE;
1561         int bad = FALSE;
1562
1563         for (i=0; i<66; i++)
1564                 if (bufp->c.b[446+i])
1565                         goto nonz;
1566
1567         /* zero table */
1568         if (!curses_started) {
1569                 fatal(_("No partition table.\n"), 3);
1570                 return;
1571         } else {
1572                 mvaddstr(WARNING_START, 0,
1573                          _("No partition table. Starting with zero table."));
1574                 putchar(BELL);
1575                 refresh();
1576                 zero_table = TRUE;
1577                 return;
1578         }
1579  nonz:
1580         if (bufp->p.magicflag[0] != PART_TABLE_FLAG0 ||
1581             bufp->p.magicflag[1] != PART_TABLE_FLAG1) {
1582                 if (!curses_started)
1583                         fatal(_("Bad signature on partition table"), 3);
1584
1585                 /* Matthew Wilcox */
1586                 mvaddstr(WARNING_START, 0,
1587                          _("Unknown partition table type"));
1588                 mvaddstr(WARNING_START+1, 0,
1589                          _("Do you wish to start with a zero table [y/N] ?"));
1590                 putchar(BELL);
1591                 refresh();
1592                 {
1593                         int cont = getch();
1594                         if (cont == EOF || !said_yes(cont))
1595                                 die_x(3);
1596                 }
1597                 zero_table = TRUE;
1598                 return;
1599         }
1600
1601         hh = ss = 0;
1602         for (i=0; i<4; i++) {
1603                 p = &(bufp->p.part[i]);
1604                 if (p->sys_ind != 0) {
1605                         h = p->end_head + 1;
1606                         s = (p->end_sector & 077);
1607                         if (first) {
1608                                 hh = h;
1609                                 ss = s;
1610                                 first = FALSE;
1611                         } else if (hh != h || ss != s)
1612                                 bad = TRUE;
1613                 }
1614         }
1615
1616         if (!first && !bad) {
1617                 pt_heads = hh;
1618                 pt_sectors = ss;
1619         }
1620 }
1621
1622 static void
1623 decide_on_geometry(void) {
1624     heads = (user_heads ? user_heads :
1625              pt_heads ? pt_heads :
1626              kern_heads ? kern_heads : 255);
1627     sectors = (user_sectors ? user_sectors :
1628                pt_sectors ? pt_sectors :
1629                kern_sectors ? kern_sectors : 63);
1630     cylinder_size = heads*sectors;
1631     cylinders = actual_size/cylinder_size;
1632     if (user_cylinders > 0)
1633             cylinders = user_cylinders;
1634
1635     total_size = cylinder_size*cylinders;
1636     if (total_size > actual_size)
1637             print_warning(_("You specified more cylinders than fit on disk"));
1638 }
1639
1640 static void
1641 clear_p_info(void) {
1642     num_parts = 1;
1643     p_info[0].first_sector = 0;
1644     p_info[0].last_sector = total_size - 1;
1645     p_info[0].offset = 0;
1646     p_info[0].flags = 0;
1647     p_info[0].id = FREE_SPACE;
1648     p_info[0].num = PRI_OR_LOG;
1649
1650     ext_info.first_sector = 0;
1651     ext_info.last_sector = 0;
1652     ext_info.offset = 0;
1653     ext_info.flags = 0;
1654     ext_info.id = FREE_SPACE;
1655     ext_info.num = PRIMARY;
1656 }
1657
1658 static void
1659 fill_p_info(void) {
1660     int pn, i;
1661     long long bs, bsz;
1662     unsigned long long llsectors;
1663     struct partition *p;
1664     partition_table buffer;
1665     partition_info tmp_ext = { 0, 0, 0, 0, FREE_SPACE, PRIMARY };
1666
1667     if ((fd = open(disk_device, O_RDWR)) < 0) {
1668          if ((fd = open(disk_device, O_RDONLY)) < 0)
1669               fatal(_("Cannot open disk drive"), 2);
1670          opentype = O_RDONLY;
1671          print_warning(_("Opened disk read-only - you have no permission to write"));
1672          if (curses_started) {
1673               refresh();
1674               getch();
1675               clear_warning();
1676          }
1677     } else
1678          opentype = O_RDWR;
1679     opened = TRUE;
1680
1681     if (gpt_probe_signature_fd(fd)) {
1682          print_warning(_("Warning!!  Unsupported GPT (GUID Partition Table) detected. Use GNU Parted."));
1683          refresh();
1684          getch();
1685          clear_warning();
1686     }
1687
1688 #ifdef BLKFLSBUF
1689     /* Blocks are visible in more than one way:
1690        e.g. as block on /dev/hda and as block on /dev/hda3
1691        By a bug in the Linux buffer cache, we will see the old
1692        contents of /dev/hda when the change was made to /dev/hda3.
1693        In order to avoid this, discard all blocks on /dev/hda.
1694        Note that partition table blocks do not live in /dev/hdaN,
1695        so this only plays a role if we want to show volume labels. */
1696     ioctl(fd, BLKFLSBUF);       /* ignore errors */
1697                                 /* e.g. Permission Denied */
1698 #endif
1699
1700     if (blkdev_get_sectors(fd, &llsectors) == -1)
1701             fatal(_("Cannot get disk size"), 3);
1702     actual_size = llsectors;
1703
1704     read_sector(buffer.c.b, 0);
1705
1706     get_kernel_geometry();
1707
1708     if (!zero_table || use_partition_table_geometry)
1709         get_partition_table_geometry(& buffer);
1710
1711     decide_on_geometry();
1712
1713     clear_p_info();
1714
1715     if (!zero_table) {
1716         char *errmsg = "";
1717
1718         for (i = 0; i < 4; i++) {
1719             p = & buffer.p.part[i];
1720             bs = get_start_sect(p);
1721             bsz = get_nr_sects(p);
1722
1723             if (p->sys_ind > 0 &&
1724                 add_part(i, p->sys_ind, p->boot_ind,
1725                          ((bs <= sectors) ? 0 : bs), bs + bsz - 1,
1726                          ((bs <= sectors) ? bs : 0), 1, &errmsg)) {
1727                     char *bad = _("Bad primary partition");
1728                     char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
1729                     sprintf(msg, "%s %d: %s", bad, i, errmsg);
1730                     fatal(msg, 4);
1731             }
1732             if (is_extended(buffer.p.part[i].sys_ind))
1733                 tmp_ext = ext_info;
1734         }
1735
1736         if (is_extended(tmp_ext.id)) {
1737             ext_info = tmp_ext;
1738             logical_sectors[logical] =
1739                  ext_info.first_sector + ext_info.offset;
1740             read_sector(buffer.c.b, logical_sectors[logical++]);
1741             i = 4;
1742             do {
1743                 for (pn = 0;
1744                      pn < 4 && (!buffer.p.part[pn].sys_ind ||
1745                                is_extended(buffer.p.part[pn].sys_ind));
1746                      pn++);
1747
1748                 if (pn < 4) {
1749                         p = & buffer.p.part[pn];
1750                         bs = get_start_sect(p);
1751                         bsz = get_nr_sects(p);
1752
1753                         if (add_part(i++, p->sys_ind, p->boot_ind,
1754                              logical_sectors[logical-1],
1755                              logical_sectors[logical-1] + bs + bsz - 1,
1756                              bs, 1, &errmsg)) {
1757                                 char *bad = _("Bad logical partition");
1758                                 char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
1759                                 sprintf(msg, "%s %d: %s", bad, i, errmsg);
1760                                 fatal(msg, 4);
1761                         }
1762                 }
1763
1764                 for (pn = 0;
1765                      pn < 4 && !is_extended(buffer.p.part[pn].sys_ind);
1766                      pn++);
1767                 if (pn < 4) {
1768                         p = & buffer.p.part[pn];
1769                         bs = get_start_sect(p);
1770                         logical_sectors[logical] = ext_info.first_sector
1771                                 + ext_info.offset + bs;
1772                         read_sector(buffer.c.b, logical_sectors[logical++]);
1773                 }
1774             } while (pn < 4 && logical < MAXIMUM_PARTS-4);
1775         }
1776     }
1777 }
1778
1779 static void
1780 fill_part_table(struct partition *p, partition_info *pi) {
1781     long long begin;
1782
1783     p->boot_ind = pi->flags;
1784     p->sys_ind = pi->id;
1785     begin = pi->first_sector + pi->offset;
1786     if (IS_LOGICAL(pi->num))
1787         set_start_sect(p,pi->offset);
1788     else
1789         set_start_sect(p,begin);
1790     set_nr_sects(p, pi->last_sector - begin + 1);
1791     set_hsc_begin(p, begin);
1792     set_hsc_end(p, pi->last_sector);
1793 }
1794
1795 static void
1796 fill_primary_table(partition_table *buffer) {
1797     int i;
1798
1799     /* Zero out existing table */
1800     for (i = 0x1BE; i < SECTOR_SIZE; i++)
1801         buffer->c.b[i] = 0;
1802
1803     for (i = 0; i < num_parts; i++)
1804         if (IS_PRIMARY(p_info[i].num))
1805             fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i]));
1806
1807     if (is_extended(ext_info.id))
1808         fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);
1809
1810     buffer->p.magicflag[0] = PART_TABLE_FLAG0;
1811     buffer->p.magicflag[1] = PART_TABLE_FLAG1;
1812 }
1813
1814 static void
1815 fill_logical_table(partition_table *buffer, partition_info *pi) {
1816     struct partition *p;
1817     int i;
1818
1819     for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++);
1820     if (i == logical || buffer->p.magicflag[0] != PART_TABLE_FLAG0
1821                      || buffer->p.magicflag[1] != PART_TABLE_FLAG1)
1822         for (i = 0; i < SECTOR_SIZE; i++)
1823             buffer->c.b[i] = 0;
1824
1825     /* Zero out existing table */
1826     for (i = 0x1BE; i < SECTOR_SIZE; i++)
1827         buffer->c.b[i] = 0;
1828
1829     fill_part_table(&(buffer->p.part[0]), pi);
1830
1831     for (i = 0;
1832          i < num_parts && pi->num != p_info[i].num - 1;
1833          i++);
1834
1835     if (i < num_parts) {
1836         p = &(buffer->p.part[1]);
1837         pi = &(p_info[i]);
1838
1839         p->boot_ind = 0;
1840         p->sys_ind = DOS_EXTENDED;
1841         set_start_sect(p, pi->first_sector - ext_info.first_sector - ext_info.offset);
1842         set_nr_sects(p, pi->last_sector - pi->first_sector + 1);
1843         set_hsc_begin(p, pi->first_sector);
1844         set_hsc_end(p, pi->last_sector);
1845     }
1846
1847     buffer->p.magicflag[0] = PART_TABLE_FLAG0;
1848     buffer->p.magicflag[1] = PART_TABLE_FLAG1;
1849 }
1850
1851 static void
1852 write_part_table(void) {
1853     int i, ct, done = FALSE, len;
1854     partition_table buffer;
1855     struct stat s;
1856     int is_bdev;
1857     char response[LINE_LENGTH];
1858
1859     if (opentype == O_RDONLY) {
1860          print_warning(_("Opened disk read-only - you have no permission to write"));
1861          refresh();
1862          getch();
1863          clear_warning();
1864          return;
1865     }
1866
1867     is_bdev = 0;
1868     if(fstat(fd, &s) == 0 && S_ISBLK(s.st_mode))
1869          is_bdev = 1;
1870
1871     if (is_bdev) {
1872          print_warning(_("Warning!!  This may destroy data on your disk!"));
1873
1874          while (!done) {
1875               mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
1876                        _("Are you sure you want to write the partition table "
1877                        "to disk? (yes or no): "));
1878               len = get_string(response, LINE_LENGTH, NULL);
1879               clear_warning();
1880               if (len == GS_ESCAPE)
1881                    return;
1882               else if (strcasecmp(response, _("no")) == 0 ||
1883                        strcasecmp(response, "no") == 0) {
1884                    print_warning(_("Did not write partition table to disk"));
1885                    return;
1886               } else if (strcasecmp(response, _("yes")) == 0 ||
1887                          strcasecmp(response, "yes") == 0)
1888                    done = TRUE;
1889               else
1890                    print_warning(_("Please enter `yes' or `no'"));
1891          }
1892
1893          clear_warning();
1894          print_warning(_("Writing partition table to disk..."));
1895          refresh();
1896     }
1897
1898     read_sector(buffer.c.b, 0);
1899     fill_primary_table(&buffer);
1900     write_sector(buffer.c.b, 0);
1901
1902     for (i = 0; i < num_parts; i++)
1903         if (IS_LOGICAL(p_info[i].num)) {
1904             read_sector(buffer.c.b, p_info[i].first_sector);
1905             fill_logical_table(&buffer, &(p_info[i]));
1906             write_sector(buffer.c.b, p_info[i].first_sector);
1907         }
1908
1909     if (is_bdev) {
1910 #ifdef BLKRRPART
1911          sync();
1912          sleep(2);
1913          if (!ioctl(fd,BLKRRPART))
1914               changed = TRUE;
1915 #endif
1916          sync();
1917          sleep(4);
1918
1919          clear_warning();
1920          if (changed)
1921               print_warning(_("Wrote partition table to disk"));
1922          else
1923               print_warning(_("Wrote partition table, but re-read table failed.  Run partprobe(8), kpartx(8) or reboot to update table."));
1924     } else
1925          print_warning(_("Wrote partition table to disk"));
1926
1927     /* Check: unique bootable primary partition? */
1928     ct = 0;
1929     for (i = 0; i < num_parts; i++)
1930         if (IS_PRIMARY(i) && p_info[i].flags == ACTIVE_FLAG)
1931             ct++;
1932     if (ct == 0)
1933         print_warning(_("No primary partitions are marked bootable. DOS MBR cannot boot this."));
1934     if (ct > 1)
1935         print_warning(_("More than one primary partition is marked bootable. DOS MBR cannot boot this."));
1936 }
1937
1938 static void
1939 fp_printf(FILE *fp, char *format, ...) {
1940     va_list args;
1941     char buf[1024];
1942     int y, x;
1943
1944     va_start(args, format);
1945     vsnprintf(buf, sizeof(buf), format, args);
1946     va_end(args);
1947
1948     if (fp == NULL) {
1949         /* The following works best if the string to be printed has at
1950            most only one newline. */
1951         printw("%s", buf);
1952         getyx(stdscr, y, x);
1953         if (y >= COMMAND_LINE_Y-2) {
1954             menuContinue();
1955             erase();
1956             move(0, 0);
1957         }
1958     } else
1959         fprintf(fp, "%s", buf);
1960 }
1961
1962 #define MAX_PER_LINE 16
1963 static void
1964 print_file_buffer(FILE *fp, unsigned char *buffer) {
1965     int i,l;
1966
1967     for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
1968         if (l == 0)
1969             fp_printf(fp, "0x%03X:", i);
1970         fp_printf(fp, " %02X", buffer[i]);
1971         if (l == MAX_PER_LINE - 1) {
1972             fp_printf(fp, "\n");
1973             l = -1;
1974         }
1975     }
1976     if (l > 0)
1977         fp_printf(fp, "\n");
1978     fp_printf(fp, "\n");
1979 }
1980
1981 static void
1982 print_raw_table(void) {
1983     int i, to_file;
1984     partition_table buffer;
1985     char fname[LINE_LENGTH];
1986     FILE *fp;
1987
1988     if (print_only) {
1989         fp = stdout;
1990         to_file = TRUE;
1991     } else {
1992         mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
1993                  _("Enter filename or press RETURN to display on screen: "));
1994
1995         if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
1996             return;
1997
1998         if (to_file) {
1999             if ((fp = fopen(fname, "w")) == NULL) {
2000                 char errstr[LINE_LENGTH];
2001                 snprintf(errstr, sizeof(errstr),
2002                          _("Cannot open file '%s'"), fname);
2003                 print_warning(errstr);
2004                 return;
2005             }
2006         } else {
2007             fp = NULL;
2008             erase();
2009             move(0, 0);
2010         }
2011     }
2012
2013     fp_printf(fp, _("Disk Drive: %s\n"), disk_device);
2014
2015     fp_printf(fp, _("Sector 0:\n"));
2016     read_sector(buffer.c.b, 0);
2017     fill_primary_table(&buffer);
2018     print_file_buffer(fp, buffer.c.b);
2019
2020     for (i = 0; i < num_parts; i++)
2021         if (IS_LOGICAL(p_info[i].num)) {
2022             fp_printf(fp, _("Sector %d:\n"), p_info[i].first_sector);
2023             read_sector(buffer.c.b, p_info[i].first_sector);
2024             fill_logical_table(&buffer, &(p_info[i]));
2025             print_file_buffer(fp, buffer.c.b);
2026         }
2027
2028     if (to_file) {
2029         if (!print_only)
2030             fclose(fp);
2031     } else {
2032         menuContinue();
2033     }
2034 }
2035
2036 static void
2037 print_p_info_entry(FILE *fp, partition_info *p) {
2038     long long size;
2039     char part_str[40];
2040
2041     if (p->id == UNUSABLE)
2042         fp_printf(fp, _("   None   "));
2043     else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG)
2044         fp_printf(fp, _("   Pri/Log"));
2045     else if (p->id == FREE_SPACE && p->num == PRIMARY)
2046         fp_printf(fp, _("   Primary"));
2047     else if (p->id == FREE_SPACE && p->num == LOGICAL)
2048         fp_printf(fp, _("   Logical"));
2049     else
2050         fp_printf(fp, "%2d %-7.7s", p->num+1,
2051                   IS_LOGICAL(p->num) ? _("Logical") : _("Primary"));
2052
2053     fp_printf(fp, " ");
2054
2055     fp_printf(fp, "%11lld%c", p->first_sector,
2056               ((p->first_sector/cylinder_size) !=
2057                ((float)p->first_sector/cylinder_size) ?
2058                '*' : ' '));
2059
2060     fp_printf(fp, "%11lld%c", p->last_sector,
2061               (((p->last_sector+1)/cylinder_size) !=
2062                ((float)(p->last_sector+1)/cylinder_size) ?
2063                '*' : ' '));
2064
2065     fp_printf(fp, "%6ld%c", p->offset,
2066               ((((p->first_sector == 0 || IS_LOGICAL(p->num)) &&
2067                  (p->offset != sectors)) ||
2068                 (p->first_sector != 0 && IS_PRIMARY(p->num) &&
2069                  p->offset != 0)) ?
2070                '#' : ' '));
2071
2072     size = p->last_sector - p->first_sector + 1;
2073     fp_printf(fp, "%11lld%c", size,
2074               ((size/cylinder_size) != ((float)size/cylinder_size) ?
2075                '*' : ' '));
2076
2077     /* fp_printf(fp, " "); */
2078
2079     if (p->id == UNUSABLE)
2080         sprintf(part_str, "%.15s", _("Unusable"));
2081     else if (p->id == FREE_SPACE)
2082         sprintf(part_str, "%.15s", _("Free Space"));
2083     else if (partition_type_name(p->id))
2084         sprintf(part_str, "%.15s (%02X)", _(partition_type_name(p->id)), p->id);
2085     else
2086         sprintf(part_str, "%.15s (%02X)", _("Unknown"), p->id);
2087     fp_printf(fp, "%-20.20s", part_str);
2088
2089     fp_printf(fp, " ");
2090
2091     if (p->flags == ACTIVE_FLAG)
2092         fp_printf(fp, _("Boot"), p->flags);
2093     else if (p->flags != 0)
2094         fp_printf(fp, _("(%02X)"), p->flags);
2095     else
2096         fp_printf(fp, _("None"), p->flags);
2097
2098     fp_printf(fp, "\n");
2099 }
2100
2101 static void
2102 print_p_info(void) {
2103     char fname[LINE_LENGTH];
2104     FILE *fp;
2105     int i, to_file, pext = is_extended(ext_info.id);
2106
2107     if (print_only) {
2108         fp = stdout;
2109         to_file = TRUE;
2110     } else {
2111         mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
2112                  _("Enter filename or press RETURN to display on screen: "));
2113
2114         if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
2115             return;
2116
2117         if (to_file) {
2118             if ((fp = fopen(fname, "w")) == NULL) {
2119                 char errstr[LINE_LENGTH];
2120                 snprintf(errstr, LINE_LENGTH, _("Cannot open file '%s'"), fname);
2121                 print_warning(errstr);
2122                 return;
2123             }
2124         } else {
2125             fp = NULL;
2126             erase();
2127             move(0, 0);
2128         }
2129     }
2130
2131     fp_printf(fp, _("Partition Table for %s\n"), disk_device);
2132     fp_printf(fp, "\n");
2133     fp_printf(fp, _("               First       Last\n"));
2134     fp_printf(fp, _(" # Type       Sector      Sector   Offset    Length   Filesystem Type (ID) Flag\n"));
2135     fp_printf(fp, _("-- ------- ----------- ----------- ------ ----------- -------------------- ----\n"));
2136
2137     for (i = 0; i < num_parts; i++) {
2138         if (pext && (p_info[i].first_sector >= ext_info.first_sector)) {
2139             print_p_info_entry(fp,&ext_info);
2140             pext = FALSE;
2141         }
2142         print_p_info_entry(fp, &(p_info[i]));
2143     }
2144
2145     if (to_file) {
2146         if (!print_only)
2147             fclose(fp);
2148     } else {
2149         menuContinue();
2150     }
2151 }
2152
2153 static void
2154 print_part_entry(FILE *fp, int num, partition_info *pi) {
2155     long long first = 0, start = 0, end = 0, size = 0;
2156     unsigned char ss, es, sh, eh;
2157     int sc, ec;
2158     int flags = 0, id = 0;
2159
2160     ss = sh = es = eh = 0;
2161     sc = ec = 0;
2162
2163     if (pi != NULL) {
2164         flags = pi->flags;
2165         id = pi->id;
2166
2167         if (IS_LOGICAL(num))
2168             first = pi->offset;
2169         else
2170             first = pi->first_sector + pi->offset;
2171
2172         start = pi->first_sector + pi->offset;
2173         end = pi->last_sector;
2174         size = end - start + 1;
2175
2176         set_hsc0(&sh, &ss, &sc, start);
2177         set_hsc0(&eh, &es, &ec, end);
2178     }
2179
2180     fp_printf(fp, "%2d  0x%02X %4d %4d %5d 0x%02X %4d %4d %5d %11lld %11lld\n",
2181               num+1, flags, sh, ss, sc, id, eh, es, ec, first, size);
2182 }
2183
2184
2185 static void
2186 print_part_table(void) {
2187     int i, j, to_file;
2188     char fname[LINE_LENGTH];
2189     FILE *fp;
2190
2191     if (print_only) {
2192         fp = stdout;
2193         to_file = TRUE;
2194     } else {
2195         mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
2196                  _("Enter filename or press RETURN to display on screen: "));
2197
2198         if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
2199             return;
2200
2201         if (to_file) {
2202             if ((fp = fopen(fname, "w")) == NULL) {
2203                 char errstr[LINE_LENGTH];
2204                 snprintf(errstr, LINE_LENGTH, _("Cannot open file '%s'"), fname);
2205                 print_warning(errstr);
2206                 return;
2207             }
2208         } else {
2209             fp = NULL;
2210             erase();
2211             move(0, 0);
2212         }
2213     }
2214
2215     fp_printf(fp, _("Partition Table for %s\n"), disk_device);
2216     fp_printf(fp, "\n");
2217     /* Three-line heading. Read "Start Sector" etc vertically. */
2218     fp_printf(fp, _("         ---Starting----      ----Ending-----    Start     Number of\n"));
2219     fp_printf(fp, _(" # Flags Head Sect  Cyl   ID  Head Sect  Cyl     Sector    Sectors\n"));
2220     fp_printf(fp, _("-- ----- ---- ---- ----- ---- ---- ---- ----- ----------- -----------\n"));
2221
2222     for (i = 0; i < 4; i++) {
2223         for (j = 0;
2224              j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i);
2225              j++);
2226         if (j < num_parts) {
2227             print_part_entry(fp, i, &(p_info[j]));
2228         } else if (is_extended(ext_info.id) && ext_info.num == i) {
2229             print_part_entry(fp, i, &ext_info);
2230         } else {
2231             print_part_entry(fp, i, NULL);
2232         }
2233     }
2234
2235     for (i = 0; i < num_parts; i++)
2236         if (IS_LOGICAL(p_info[i].num))
2237             print_part_entry(fp, p_info[i].num, &(p_info[i]));
2238
2239     if (to_file) {
2240         if (!print_only)
2241             fclose(fp);
2242     } else {
2243         menuContinue();
2244     }
2245 }
2246
2247 static void
2248 print_tables(void) {
2249     int done = FALSE;
2250
2251     static struct MenuItem menuFormat[]=
2252     {
2253         { 'r', N_("Raw"), N_("Print the table using raw data format") },
2254         { 's', N_("Sectors"), N_("Print the table ordered by sectors") },
2255         { 't', N_("Table"), N_("Just print the partition table") },
2256         { ESC, N_("Cancel"), N_("Don't print the table") },
2257         { 0, NULL, NULL }
2258     };
2259
2260     while (!done)
2261         switch ( toupper(menuSimple( menuFormat, 2)) ) {
2262         case 'R':
2263             print_raw_table();
2264             done = TRUE;
2265             break;
2266         case 'S':
2267             print_p_info();
2268             done = TRUE;
2269             break;
2270         case 'T':
2271             print_part_table();
2272             done = TRUE;
2273             break;
2274         case ESC:
2275             done = TRUE;
2276             break;
2277         }
2278 }
2279
2280 #define END_OF_HELP "EOHS!"
2281 static void
2282 display_help(void) {
2283     char *help_text[] = {
2284         N_("Help Screen for cfdisk"),
2285         "",
2286         N_("This is cfdisk, a curses based disk partitioning program, which"),
2287         N_("allows you to create, delete and modify partitions on your hard"),
2288         N_("disk drive."),
2289         "",
2290         N_("Copyright (C) 1994-1999 Kevin E. Martin & aeb"),
2291         "",
2292         N_("Command      Meaning"),
2293         N_("-------      -------"),
2294         N_("  b          Toggle bootable flag of the current partition"),
2295         N_("  d          Delete the current partition"),
2296         N_("  g          Change cylinders, heads, sectors-per-track parameters"),
2297         N_("             WARNING: This option should only be used by people who"),
2298         N_("             know what they are doing."),
2299         N_("  h          Print this screen"),
2300         N_("  m          Maximize disk usage of the current partition"),
2301         N_("             Note: This may make the partition incompatible with"),
2302         N_("             DOS, OS/2, ..."),
2303         N_("  n          Create new partition from free space"),
2304         N_("  p          Print partition table to the screen or to a file"),
2305         N_("             There are several different formats for the partition"),
2306         N_("             that you can choose from:"),
2307         N_("                r - Raw data (exactly what would be written to disk)"),
2308         N_("                s - Table ordered by sectors"),
2309         N_("                t - Table in raw format"),
2310         N_("  q          Quit program without writing partition table"),
2311         N_("  t          Change the filesystem type"),
2312         N_("  u          Change units of the partition size display"),
2313         N_("             Rotates through MB, sectors and cylinders"),
2314         N_("  W          Write partition table to disk (must enter upper case W)"),
2315         N_("             Since this might destroy data on the disk, you must"),
2316         N_("             either confirm or deny the write by entering `yes' or"),
2317         N_("             `no'"),
2318         N_("Up Arrow     Move cursor to the previous partition"),
2319         N_("Down Arrow   Move cursor to the next partition"),
2320         N_("CTRL-L       Redraws the screen"),
2321         N_("  ?          Print this screen"),
2322         "",
2323         N_("Note: All of the commands can be entered with either upper or lower"),
2324         N_("case letters (except for Writes)."),
2325         END_OF_HELP
2326     };
2327
2328     int cur_line = 0;
2329     FILE *fp = NULL;
2330
2331     erase();
2332     move(0, 0);
2333     while (strcmp(help_text[cur_line], END_OF_HELP)) {
2334         if (help_text[cur_line][0])
2335             fp_printf(fp, "%s\n", _(help_text[cur_line]));
2336         else
2337             fp_printf(fp, "\n");
2338         cur_line++;
2339     }
2340     menuContinue();
2341 }
2342
2343 static int
2344 change_geometry(void) {
2345     int ret_val = FALSE;
2346     int done = FALSE;
2347     char def[LINE_LENGTH];
2348     char response[LINE_LENGTH];
2349     long long tmp_val;
2350     int i;
2351
2352     while (!done) {
2353         static struct MenuItem menuGeometry[]=
2354         {
2355             { 'c', N_("Cylinders"), N_("Change cylinder geometry") },
2356             { 'h', N_("Heads"), N_("Change head geometry") },
2357             { 's', N_("Sectors"), N_("Change sector geometry") },
2358             { 'd', N_("Done"), N_("Done with changing geometry") },
2359             { 0, NULL, NULL }
2360         };
2361         move(COMMAND_LINE_Y, COMMAND_LINE_X);
2362         clrtoeol();
2363         refresh();
2364
2365         clear_warning();
2366
2367         switch (toupper( menuSimple(menuGeometry, 3) )) {
2368         case 'C':
2369             sprintf(def, "%llu", actual_size/cylinder_size);
2370             mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
2371                      _("Enter the number of cylinders: "));
2372             i = get_string(response, LINE_LENGTH, def);
2373             if (i == GS_DEFAULT) {
2374                 user_cylinders = actual_size/cylinder_size;
2375                 ret_val = TRUE;
2376             } else if (i > 0) {
2377                 tmp_val = atoll(response);
2378                 if (tmp_val > 0) {
2379                     user_cylinders = tmp_val;
2380                     ret_val = TRUE;
2381                 } else
2382                     print_warning(_("Illegal cylinders value"));
2383             }
2384             break;
2385         case 'H':
2386             sprintf(def, "%d", heads);
2387             mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
2388                      _("Enter the number of heads: "));
2389             if (get_string(response, LINE_LENGTH, def) > 0) {
2390                 tmp_val = atoll(response);
2391                 if (tmp_val > 0 && tmp_val <= MAX_HEADS) {
2392                     user_heads = tmp_val;
2393                     ret_val = TRUE;
2394                 } else
2395                     print_warning(_("Illegal heads value"));
2396             }
2397             break;
2398         case 'S':
2399             sprintf(def, "%d", sectors);
2400             mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
2401                      _("Enter the number of sectors per track: "));
2402             if (get_string(response, LINE_LENGTH, def) > 0) {
2403                 tmp_val = atoll(response);
2404                 if (tmp_val > 0 && tmp_val <= MAX_SECTORS) {
2405                     user_sectors = tmp_val;
2406                     ret_val = TRUE;
2407                 } else
2408                     print_warning(_("Illegal sectors value"));
2409             }
2410             break;
2411         case ESC:
2412         case 'D':
2413             done = TRUE;
2414             break;
2415         default:
2416             putchar(BELL);
2417             break;
2418         }
2419
2420         if (ret_val) {
2421             decide_on_geometry();
2422             draw_screen();
2423         }
2424     }
2425
2426     if (ret_val) {
2427         long long disk_end;
2428
2429         disk_end = total_size-1;
2430
2431         if (p_info[num_parts-1].last_sector > disk_end) {
2432             while (p_info[num_parts-1].first_sector > disk_end) {
2433                 if (p_info[num_parts-1].id == FREE_SPACE ||
2434                     p_info[num_parts-1].id == UNUSABLE)
2435                     remove_part(num_parts-1);
2436                 else
2437                     del_part(num_parts-1);
2438             }
2439
2440             p_info[num_parts-1].last_sector = disk_end;
2441
2442             if (ext_info.last_sector > disk_end)
2443                 ext_info.last_sector = disk_end;
2444         } else if (p_info[num_parts-1].last_sector < disk_end) {
2445             if (p_info[num_parts-1].id == FREE_SPACE ||
2446                 p_info[num_parts-1].id == UNUSABLE) {
2447                 p_info[num_parts-1].last_sector = disk_end;
2448             } else {
2449                 insert_empty_part(num_parts,
2450                                   p_info[num_parts-1].last_sector+1,
2451                                   disk_end);
2452             }
2453         }
2454
2455         /* Make sure the partitions are correct */
2456         check_part_info();
2457     }
2458
2459     return ret_val;
2460 }
2461
2462 static void
2463 change_id(int i) {
2464     char id[LINE_LENGTH], def[LINE_LENGTH];
2465     int num_types = 0;
2466     int num_across, num_down;
2467     int len, new_id = ((p_info[i].id == LINUX) ? LINUX_SWAP : LINUX);
2468     int y_start, y_end, row, row_min, row_max, row_offset, j, needmore;
2469
2470     for (j = 1; i386_sys_types[j].name; j++) ;
2471     num_types = j-1;            /* do not count the Empty type */
2472
2473     num_across = COLS/COL_ID_WIDTH;
2474     num_down = (((float)num_types)/num_across + 1);
2475     y_start = COMMAND_LINE_Y - 1 - num_down;
2476     if (y_start < 1) {
2477         y_start = 1;
2478         y_end = COMMAND_LINE_Y - 2;
2479     } else {
2480         if (y_start > DISK_TABLE_START+cur_part+4)
2481             y_start = DISK_TABLE_START+cur_part+4;
2482         y_end = y_start + num_down - 1;
2483     }
2484
2485     row_min = 1;
2486     row_max = COMMAND_LINE_Y - 2;
2487     row_offset = 0;
2488     do {
2489         for (j = y_start - 1; j <= y_end + 1; j++) {
2490             move(j, 0);
2491             clrtoeol();
2492         }
2493         needmore = 0;
2494         for (j = 1; i386_sys_types[j].name; j++) {
2495             row = y_start + (j-1) % num_down - row_offset;
2496             if (row >= row_min && row <= row_max) {
2497                 move(row, ((j-1)/num_down)*COL_ID_WIDTH + 1);
2498                 printw("%02X %-20.20s",
2499                        i386_sys_types[j].type,
2500                        _(i386_sys_types[j].name));
2501             }
2502             if (row > row_max)
2503                 needmore = 1;
2504         }
2505         if (needmore)
2506                 menuContinue();
2507         row_offset += (row_max - row_min + 1);
2508     } while(needmore);
2509
2510     sprintf(def, "%02X", new_id);
2511     mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Enter filesystem type: "));
2512     if ((len = get_string(id, 2, def)) <= 0 && len != GS_DEFAULT)
2513         return;
2514
2515     if (len != GS_DEFAULT) {
2516         if (!isxdigit(id[0]))
2517             return;
2518         new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10);
2519         if (len == 2) {
2520             if (isxdigit(id[1]))
2521                 new_id = new_id*16 +
2522                     (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10);
2523             else
2524                 return;
2525         }
2526     }
2527
2528     if (new_id == 0)
2529         print_warning(_("Cannot change FS Type to empty"));
2530     else if (is_extended(new_id))
2531         print_warning(_("Cannot change FS Type to extended"));
2532     else
2533         p_info[i].id = new_id;
2534 }
2535
2536 static void
2537 draw_partition(int i) {
2538     int j;
2539     int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
2540     char *t;
2541     long long size;
2542     double fsize;
2543
2544     if (!arrow_cursor) {
2545         move(y, 0);
2546         for (j = 0; j < COLS; j++)
2547             addch(' ');
2548     }
2549
2550     if (p_info[i].id > 0) {
2551         char *dbn = my_basename(disk_device);
2552         int l = strlen(dbn);
2553         int digit_last = isdigit(dbn[l-1]);
2554
2555         mvprintw(y, NAME_START,
2556                  "%s%s%d", dbn, (digit_last ? "p" : ""),
2557                  p_info[i].num+1);
2558         if (p_info[i].flags) {
2559             if (p_info[i].flags == ACTIVE_FLAG)
2560                 mvaddstr(y, FLAGS_START, _("Boot"));
2561             else
2562                 mvprintw(y, FLAGS_START, _("Unk(%02X)"), p_info[i].flags);
2563             if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
2564                 if (p_info[i].offset != sectors)
2565                     addstr(_(", NC"));
2566             } else {
2567                 if (p_info[i].offset != 0)
2568                     addstr(_(", NC"));
2569             }
2570         } else {
2571             if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
2572                 if (p_info[i].offset != sectors)
2573                     mvaddstr(y, FLAGS_START, _("NC"));
2574             } else {
2575                 if (p_info[i].offset != 0)
2576                     mvaddstr(y, FLAGS_START, _("NC"));
2577             }
2578         }
2579     }
2580     mvaddstr(y, PTYPE_START,
2581              (p_info[i].id == UNUSABLE ? "" :
2582               (IS_LOGICAL(p_info[i].num) ? _("Logical") :
2583                (p_info[i].num >= 0 ? _("Primary") :
2584                 (p_info[i].num == PRI_OR_LOG ? _("Pri/Log") :
2585                  (p_info[i].num == PRIMARY ? _("Primary") : _("Logical")))))));
2586
2587     t = partition_type_text(i);
2588     if (t)
2589          mvaddstr(y, FSTYPE_START, t);
2590     else
2591          mvprintw(y, FSTYPE_START, _("Unknown (%02X)"), p_info[i].id);
2592
2593     if (p_info[i].volume_label[0]) {
2594         int l = strlen(p_info[i].volume_label);
2595         int s = SIZE_START-5-l;
2596         mvprintw(y, (s > LABEL_START) ? LABEL_START : s,
2597                  " [%s]  ", p_info[i].volume_label);
2598     }
2599
2600     size = p_info[i].last_sector - p_info[i].first_sector + 1;
2601     fsize = (double) size * SECTOR_SIZE;
2602     if (display_units == SECTORS)
2603         mvprintw(y, SIZE_START, "%11lld", size);
2604     else if (display_units == CYLINDERS)
2605         mvprintw(y, SIZE_START, "%11lld", size/cylinder_size);
2606     else if (display_units == MEGABYTES)
2607         mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K))/100);
2608     else if (display_units == GIGABYTES)
2609         mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K*K))/100);
2610     if (size % cylinder_size != 0 ||
2611         p_info[i].first_sector % cylinder_size != 0)
2612         mvprintw(y, COLUMNS-1, "*");
2613 }
2614
2615 static void
2616 init_const(void) {
2617     if (!defined) {
2618         NAME_START = (((float)NAME_START)/COLUMNS)*COLS;
2619         FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS;
2620         PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS;
2621         FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS;
2622         LABEL_START = (((float)LABEL_START)/COLUMNS)*COLS;
2623         SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS;
2624         COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS;
2625
2626         COMMAND_LINE_Y = LINES - 4;
2627         WARNING_START = LINES - 2;
2628
2629         if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0)
2630             NUM_ON_SCREEN = 1;
2631
2632         COLUMNS = COLS;
2633         defined = TRUE;
2634     }
2635 }
2636
2637 static void
2638 draw_screen(void) {
2639     int i;
2640     char *line;
2641
2642     line = (char *) xmalloc((COLS+1)*sizeof(char));
2643
2644     if (warning_last_time) {
2645         for (i = 0; i < COLS; i++) {
2646             move(WARNING_START, i);
2647             line[i] = inch();
2648         }
2649         line[COLS] = 0;
2650     }
2651
2652     erase();
2653
2654     if (warning_last_time)
2655         mvaddstr(WARNING_START, 0, line);
2656
2657
2658     snprintf(line, COLS+1, "cfdisk (%s)", PACKAGE_STRING);
2659     mvaddstr(HEADER_START, (COLS-strlen(line))/2, line);
2660     snprintf(line, COLS+1, _("Disk Drive: %s"), disk_device);
2661     mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line);
2662     {
2663             long long bytes = actual_size*(long long) SECTOR_SIZE;
2664             long long megabytes = bytes/(K*K);
2665
2666             if (megabytes < 10000)
2667                     sprintf(line, _("Size: %lld bytes, %lld MB"),
2668                             bytes, megabytes);
2669             else
2670                     sprintf(line, _("Size: %lld bytes, %lld.%lld GB"),
2671                             bytes, megabytes/K, (10*megabytes/K)%10);
2672     }
2673     mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line);
2674     snprintf(line, COLS+1, _("Heads: %d   Sectors per Track: %d   Cylinders: %lld"),
2675             heads, sectors, cylinders);
2676     mvaddstr(HEADER_START+4, (COLS-strlen(line))/2, line);
2677
2678     mvaddstr(DISK_TABLE_START, NAME_START, _("Name"));
2679     mvaddstr(DISK_TABLE_START, FLAGS_START, _("Flags"));
2680     mvaddstr(DISK_TABLE_START, PTYPE_START-1, _("Part Type"));
2681     mvaddstr(DISK_TABLE_START, FSTYPE_START, _("FS Type"));
2682     mvaddstr(DISK_TABLE_START, LABEL_START+1, _("[Label]"));
2683     if (display_units == SECTORS)
2684         mvaddstr(DISK_TABLE_START, SIZE_START, _("    Sectors"));
2685     else if (display_units == CYLINDERS)
2686         mvaddstr(DISK_TABLE_START, SIZE_START, _("  Cylinders"));
2687     else if (display_units == MEGABYTES)
2688         mvaddstr(DISK_TABLE_START, SIZE_START, _("  Size (MB)"));
2689     else if (display_units == GIGABYTES)
2690         mvaddstr(DISK_TABLE_START, SIZE_START, _("  Size (GB)"));
2691
2692     move(DISK_TABLE_START+1, 1);
2693     for (i = 1; i < COLS-1; i++)
2694         addch('-');
2695
2696     if (NUM_ON_SCREEN >= num_parts)
2697         for (i = 0; i < num_parts; i++)
2698             draw_partition(i);
2699     else
2700         for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
2701              i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN &&
2702              i < num_parts;
2703              i++)
2704             draw_partition(i);
2705
2706     free(line);
2707 }
2708
2709 static int
2710 draw_cursor(int move) {
2711     if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts))
2712         return -1;
2713
2714     if (arrow_cursor)
2715         mvaddstr(DISK_TABLE_START + cur_part + 2
2716                  - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "   ");
2717     else
2718         draw_partition(cur_part);
2719
2720     cur_part += move;
2721
2722     if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN !=
2723         (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN)
2724         draw_screen();
2725
2726     if (arrow_cursor)
2727         mvaddstr(DISK_TABLE_START + cur_part + 2
2728                  - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->");
2729     else {
2730         standout();
2731         draw_partition(cur_part);
2732         standend();
2733     }
2734
2735     return 0;
2736 }
2737
2738 static void
2739 do_curses_fdisk(void) {
2740     int done = FALSE;
2741     char command;
2742
2743     static struct MenuItem menuMain[] = {
2744         { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
2745         { 'd', N_("Delete"), N_("Delete the current partition") },
2746         { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
2747         { 'h', N_("Help"), N_("Print help screen") },
2748         { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
2749         { 'n', N_("New"), N_("Create new partition from free space") },
2750         { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
2751         { 'q', N_("Quit"), N_("Quit program without writing partition table") },
2752         { 't', N_("Type"), N_("Change the filesystem type (DOS, Linux, OS/2 and so on)") },
2753         { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
2754         { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
2755         { 0, NULL, NULL }
2756     };
2757     curses_started = 1;
2758     initscr();
2759     init_const();
2760
2761     old_SIGINT = signal(SIGINT, die);
2762     old_SIGTERM = signal(SIGTERM, die);
2763 #ifdef DEBUG
2764     signal(SIGINT, old_SIGINT);
2765     signal(SIGTERM, old_SIGTERM);
2766 #endif
2767
2768     cbreak();
2769     noecho();
2770     nonl();
2771
2772     fill_p_info();
2773
2774     draw_screen();
2775
2776     while (!done) {
2777         char *s;
2778
2779         (void)draw_cursor(0);
2780
2781         if (p_info[cur_part].id == FREE_SPACE) {
2782             s = ((opentype == O_RDWR) ? "hnpquW" : "hnpqu");
2783             command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
2784                 s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
2785         } else if (p_info[cur_part].id > 0) {
2786             s = ((opentype == O_RDWR) ? "bdhmpqtuW" : "bdhmpqtu");
2787             command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
2788                 s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
2789         } else {
2790             s = ((opentype == O_RDWR) ? "hpquW" : "hpqu");
2791             command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
2792                 s, MENU_HORIZ | MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
2793         }
2794         switch ( command ) {
2795         case 'B':
2796         case 'b':
2797             if (p_info[cur_part].id > 0)
2798                 p_info[cur_part].flags ^= 0x80;
2799             else
2800                 print_warning(_("Cannot make this partition bootable"));
2801             break;
2802         case 'D':
2803         case 'd':
2804             if (p_info[cur_part].id > 0) {
2805                 del_part(cur_part);
2806                 if (cur_part >= num_parts)
2807                     cur_part = num_parts - 1;
2808                 draw_screen();
2809             } else
2810                 print_warning(_("Cannot delete an empty partition"));
2811             break;
2812         case 'G':
2813         case 'g':
2814             if (change_geometry())
2815                 draw_screen();
2816             break;
2817         case 'M':
2818         case 'm':
2819             if (p_info[cur_part].id > 0) {
2820                 if (p_info[cur_part].first_sector == 0 ||
2821                     IS_LOGICAL(p_info[cur_part].num)) {
2822                     if (p_info[cur_part].offset == sectors)
2823                         p_info[cur_part].offset = 1;
2824                     else
2825                         p_info[cur_part].offset = sectors;
2826                     draw_screen();
2827                 } else if (p_info[cur_part].offset != 0)
2828                     p_info[cur_part].offset = 0;
2829                 else
2830                     print_warning(_("Cannot maximize this partition"));
2831             } else
2832                 print_warning(_("Cannot maximize this partition"));
2833             break;
2834         case 'N':
2835         case 'n':
2836             if (p_info[cur_part].id == FREE_SPACE) {
2837                 new_part(cur_part);
2838                 draw_screen();
2839             } else if (p_info[cur_part].id == UNUSABLE)
2840                 print_warning(_("This partition is unusable"));
2841             else
2842                 print_warning(_("This partition is already in use"));
2843             break;
2844         case 'P':
2845         case 'p':
2846             print_tables();
2847             draw_screen();
2848             break;
2849         case 'Q':
2850         case 'q':
2851             done = TRUE;
2852             break;
2853         case 'T':
2854         case 't':
2855             if (p_info[cur_part].id > 0) {
2856                 change_id(cur_part);
2857                 draw_screen();
2858             } else
2859                 print_warning(_("Cannot change the type of an empty partition"));
2860             break;
2861         case 'U':
2862         case 'u':
2863             if (display_units == GIGABYTES)
2864                 display_units = MEGABYTES;
2865             else if (display_units == MEGABYTES)
2866                 display_units = SECTORS;
2867             else if (display_units == SECTORS)
2868                 display_units = CYLINDERS;
2869             else if (display_units == CYLINDERS)
2870                 display_units = MEGABYTES;      /* not yet GIGA */
2871             draw_screen();
2872             break;
2873         case 'W':
2874             write_part_table();
2875             break;
2876         case 'H':
2877         case 'h':
2878         case '?':
2879             display_help();
2880             draw_screen();
2881             break;
2882         case MENU_UP : /* Up arrow */
2883             if (!draw_cursor(-1))
2884                 command = 0;
2885             else
2886                 print_warning(_("No more partitions"));
2887             break;
2888         case MENU_DOWN : /* Down arrow */
2889             if (!draw_cursor(1))
2890                 command = 0;
2891             else
2892                 print_warning(_("No more partitions"));
2893             break;
2894         case REDRAWKEY:
2895             clear();
2896             draw_screen();
2897             break;
2898         default:
2899             print_warning(_("Illegal command"));
2900             putchar(BELL); /* CTRL-G */
2901         }
2902     }
2903
2904     die_x(0);
2905 }
2906
2907 static void
2908 copyright(void) {
2909     fprintf(stderr, _("Copyright (C) 1994-2002 Kevin E. Martin & aeb\n"));
2910 }
2911
2912 static void
2913 usage(char *prog_name) {
2914     /* Unfortunately, xgettext does not handle multi-line strings */
2915     /* so, let's use explicit \n's instead */
2916     fprintf(stderr, _("\n"
2917 "Usage:\n"
2918 "Print version:\n"
2919 "        %s -v\n"
2920 "Print partition table:\n"
2921 "        %s -P {r|s|t} [options] device\n"
2922 "Interactive use:\n"
2923 "        %s [options] device\n"
2924 "\n"
2925 "Options:\n"
2926 "-a: Use arrow instead of highlighting;\n"
2927 "-z: Start with a zero partition table, instead of reading the pt from disk;\n"
2928 "-c C -h H -s S: Override the kernel's idea of the number of cylinders,\n"
2929 "                the number of heads and the number of sectors/track.\n\n"),
2930     prog_name, prog_name, prog_name);
2931
2932     copyright();
2933 }
2934
2935 int
2936 main(int argc, char **argv)
2937 {
2938     int c;
2939     int i, len;
2940
2941     setlocale(LC_ALL, "");
2942     bindtextdomain(PACKAGE, LOCALEDIR);
2943     textdomain(PACKAGE);
2944
2945     while ((c = getopt(argc, argv, "ac:gh:s:vzP:")) != -1)
2946         switch (c) {
2947         case 'a':
2948             arrow_cursor = TRUE;
2949             break;
2950         case 'c':
2951             user_cylinders = cylinders = atoll(optarg);
2952             if (cylinders <= 0) {
2953                 fprintf(stderr, "%s: %s\n", argv[0], _("Illegal cylinders value"));
2954                 exit(1);
2955             }
2956             break;
2957         case 'g':
2958             use_partition_table_geometry = TRUE;
2959             break;
2960         case 'h':
2961             user_heads = heads = atoi(optarg);
2962             if (heads <= 0 || heads > MAX_HEADS) {
2963                 fprintf(stderr, "%s: %s\n", argv[0], _("Illegal heads value"));
2964                 exit(1);
2965             }
2966             break;
2967         case 's':
2968             user_sectors = sectors = atoi(optarg);
2969             if (sectors <= 0 || sectors > MAX_SECTORS) {
2970                 fprintf(stderr, "%s: %s\n", argv[0], _("Illegal sectors value"));
2971                 exit(1);
2972             }
2973             break;
2974         case 'v':
2975             fprintf(stderr, "cfdisk (%s)\n", PACKAGE_STRING);
2976             copyright();
2977             exit(0);
2978         case 'z':
2979             zero_table = TRUE;
2980             break;
2981         case 'P':
2982             len = strlen(optarg);
2983             for (i = 0; i < len; i++) {
2984                 switch (optarg[i]) {
2985                 case 'r':
2986                     print_only |= PRINT_RAW_TABLE;
2987                     break;
2988                 case 's':
2989                     print_only |= PRINT_SECTOR_TABLE;
2990                     break;
2991                 case 't':
2992                     print_only |= PRINT_PARTITION_TABLE;
2993                     break;
2994                 default:
2995                     usage(argv[0]);
2996                     exit(1);
2997                 }
2998             }
2999             break;
3000         default:
3001             usage(argv[0]);
3002             exit(1);
3003         }
3004
3005     if (argc-optind == 1)
3006         disk_device = argv[optind];
3007     else if (argc-optind != 0) {
3008         usage(argv[0]);
3009         exit(1);
3010     } else if ((fd = open(DEFAULT_DEVICE, O_RDONLY)) < 0)
3011         disk_device = ALTERNATE_DEVICE;
3012     else close(fd);
3013
3014     if (print_only) {
3015         fill_p_info();
3016         if (print_only & PRINT_RAW_TABLE)
3017             print_raw_table();
3018         if (print_only & PRINT_SECTOR_TABLE)
3019             print_p_info();
3020         if (print_only & PRINT_PARTITION_TABLE)
3021             print_part_table();
3022     } else
3023         do_curses_fdisk();
3024
3025     return 0;
3026 }