]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
cfdisk: basic UI stuff
authorKarel Zak <kzak@redhat.com>
Thu, 9 Jan 2014 13:49:45 +0000 (14:49 +0100)
committerKarel Zak <kzak@redhat.com>
Tue, 11 Mar 2014 10:35:13 +0000 (11:35 +0100)
fdisks/Makemodule.am
fdisks/cfdisk.c

index 239e8aee0edff4d2a01bfc99f2414eeec8b52a2b..6bc842dcfd2f914499cbf4a06147e471d27f484b 100644 (file)
@@ -56,8 +56,13 @@ if BUILD_CFDISK
 sbin_PROGRAMS += cfdisk
 dist_man_MANS += fdisks/cfdisk.8
 cfdisk_SOURCES = fdisks/cfdisk.c
-cfdisk_CFLAGS = $(AM_CFLAGS)
-cfdisk_LDADD = $(LDADD) libcommon.la
+cfdisk_LDADD = $(LDADD) libcommon.la libfdisk.la
+cfdisk_CFLAGS = $(AM_CFLAGS) -I$(ul_libfdisk_incdir)
+
+if BUILD_LIBUUID
+cfdisk_CFLAGS += -I$(ul_libuuid_incdir)
+cfdisk_LDADD += libuuid.la
+endif
 
 if BUILD_LIBBLKID
 cfdisk_CFLAGS += -I$(ul_libblkid_incdir)
index 1a95c1e0b79c04db3319ab2996777350a8e6aa38..50c38a41df5545d1771fcc8ff2516881fff414e3 100644 (file)
@@ -1,69 +1,7 @@
-/****************************************************************************
- *
- *     CFDISK
- *
- * cfdisk is a curses based disk drive partitioning program that can
- * create partitions for a wide variety of operating systems including
- * Linux, MS-DOS and OS/2.
- *
- * cfdisk was inspired by the fdisk program, by A. V. Le Blanc
- * (LeBlanc@mcc.ac.uk).
- *
- *     Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
- *
- * cfdisk is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * cfdisk is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Created:    Fri Jan 28 22:46:58 1994, martin@cs.unc.edu
- * >2GB patches: Sat Feb 11 09:08:10 1995, faith@cs.unc.edu
- * Prettier menus: Sat Feb 11 09:08:25 1995, Janne Kukonlehto
- *                                           <jtklehto@stekt.oulu.fi>
- * Versions 0.8e-p: aeb@cwi.nl
- * Rebaptised 2.9p, following util-linux versioning.
- *
- *  Recognition of NTFS / HPFS difference inspired by patches
- *  from Marty Leisner <leisner@sdsp.mc.xerox.com>
- *  Exit codes by Enrique Zanardi <ezanardi@ull.es>:
- *     0: all went well
- *     1: command line error, out of memory
- *     2: hardware problems [Cannot open/seek/read/write disk drive].
- *     3: ioctl(fd, HDIO_GETGEO,...) failed. (Probably it is not a disk.)
- *     4: bad partition table on disk. [Bad primary/logical partition].
- *
- * Sat, 23 Jan 1999 19:34:45 +0100 <Vincent.Renardias@ldsol.com>
- *  Internationalized + provided initial French translation.
- * Sat Mar 20 09:26:34 EST 1999 <acme@conectiva.com.br>
- *  Some more i18n.
- * Sun Jul 18 03:19:42 MEST 1999 <aeb@cwi.nl>
- *  Terabyte-sized disks.
- * Sat Jun 30 05:23:19 EST 2001 <nathans@sgi.com>
- *  XFS label recognition.
- * Thu Nov 22 15:42:56 CET 2001 <flavio.stanchina@tin.it>
- *  ext3 and ReiserFS recognition.
- * Sun Oct 12 17:43:43 CEST 2003 <flavio.stanchina@tin.it>
- *  JFS recognition; ReiserFS label recognition.
- *
- ****************************************************************************/
-
 #include <stdlib.h>
 #include <stdio.h>
-#include <stdarg.h>
-#include <unistd.h>
-#include <ctype.h>
 #include <errno.h>
-#include <getopt.h>
-#include <fcntl.h>
+#include <signal.h>
 
 #ifdef HAVE_SLANG_H
 #include <slang.h>
 #include <ncurses/ncurses.h>
 #endif
 
-#include <signal.h>
-#include <math.h>
-#include <string.h>
-#include <sys/stat.h>
-#include <sys/ioctl.h>
-
-#ifdef HAVE_LIBBLKID
-#include <blkid.h>
-#endif
-
 #ifdef HAVE_WIDECHAR
 #include <wctype.h>
 #endif
 
+#include "c.h"
 #include "closestream.h"
 #include "nls.h"
-#include "rpmatch.h"
-#include "blkdev.h"
 #include "strutils.h"
+#include "xalloc.h"
 #include "mbsalign.h"
-#include "widechar.h"
 
-struct systypes {
-       unsigned char type;
-       char *name;
+#include "fdiskP.h"
+
+#define ARROW_CURSOR_STRING    ">>> "
+#define ARROW_CURSOR_DUMMY     "    "
+#define ARROW_CURSOR_WIDTH     (sizeof(ARROW_CURSOR_STRING) - 1)
+
+#define MENU_PADDING           2
+#define TABLE_START_LINE       4
+#define MENU_START_LINE                (LINES - 5)
+
+
+struct cfdisk_menudesc {
+       int             key;            /* keyboard shortcut */
+       const char      *name;          /* item name */
+       const char      *desc;          /* item description */
 };
 
-static struct systypes i386_sys_types[] = {
-       #include "pt-mbr-partnames.h"
+struct cfdisk_menu {
+       struct cfdisk_menudesc  *desc;
+       char                    *ignore;
+       size_t                  id;
+       size_t                  width;
+       size_t                  nitems;
+       struct cfdisk_menu      *prev;
 };
 
-#ifdef __GNU__
-#define DEFAULT_DEVICE "/dev/hd0"
-#define ALTERNATE_DEVICE "/dev/sd0"
-#elif defined(__FreeBSD__)
-#define DEFAULT_DEVICE "/dev/ad0"
-#define ALTERNATE_DEVICE "/dev/da0"
-#else
-#define DEFAULT_DEVICE "/dev/hda"
-#define ALTERNATE_DEVICE "/dev/sda"
-#endif
+static struct cfdisk_menudesc menu_main[] = {
+       { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
+       { 'd', N_("Delete"), N_("Delete the current partition") },
+//     { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
+//     { 'h', N_("Help"), N_("Print help screen") },
+//     { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
+       { 'n', N_("New"), N_("Create new partition from free space") },
+//     { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
+       { 'q', N_("Quit"), N_("Quit program without writing partition table") },
+       { 't', N_("Type"), N_("Change the partition type") },
+//     { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
+       { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
+       { 0, NULL, NULL }
+};
 
-/* With K=1024 we have `binary' megabytes, gigabytes, etc.
-   Some misguided hackers like that.
-   With K=1000 we have MB and GB that follow the standards
-   [SI, ATA, IEEE etc] and the disk manufacturers and the law. */
-#define K      1000
-
-#define LINE_LENGTH 80
-#define MAXIMUM_PARTS 60
-
-#define SECTOR_SIZE 512
-
-#define MAX_HEADS 256
-#define MAX_SECTORS 63
-
-#define ACTIVE_FLAG 0x80
-#define PART_TABLE_FLAG0 0x55
-#define PART_TABLE_FLAG1 0xAA
-
-#define UNUSABLE -1
-#define FREE_SPACE     0x00
-#define DOS_EXTENDED   0x05
-#define OS2_OR_NTFS    0x07
-#define WIN98_EXTENDED 0x0f
-#define LINUX_EXTENDED 0x85
-#define LINUX_MINIX    0x81
-#define LINUX_SWAP     0x82
-#define LINUX          0x83
-
-#define PRI_OR_LOG -1
-#define PRIMARY -2
-#define LOGICAL -3
-
-#define COL_ID_WIDTH 25
-
-#define ESC '\033'
-#define DEL '\177'
-#define BELL '\007'
-#define REDRAWKEY '\014'       /* ^L */
-
-/* Display units */
-#define GIGABYTES 1
-#define MEGABYTES 2
-#define SECTORS 3
-#define CYLINDERS 4
-
-#define GS_DEFAULT -1
-#define GS_ESCAPE -2
-
-#define PRINT_RAW_TABLE 1
-#define PRINT_SECTOR_TABLE 2
-#define PRINT_PARTITION_TABLE 4
-
-#define IS_PRIMARY(p) ((p) >= 0 && (p) < 4)
-#define IS_LOGICAL(p) ((p) > 3)
-
-#define round_int(d) ((double)((int)(d+0.5)))
-#define ceiling(d) ((double)(((d) != (int)(d)) ? (int)(d+1.0) : (int)(d)))
-
-struct partition {
-        unsigned char boot_ind;         /* 0x80 - active */
-        unsigned char head;             /* starting head */
-        unsigned char sector;           /* starting sector */
-        unsigned char cyl;              /* starting cylinder */
-        unsigned char sys_ind;          /* What partition type */
-        unsigned char end_head;         /* end head */
-        unsigned char end_sector;       /* end sector */
-        unsigned char end_cyl;          /* end cylinder */
-        unsigned char start4[4];        /* starting sector counting from 0 */
-        unsigned char size4[4];         /* nr of sectors in partition */
+enum {
+       CFDISK_MENU_MAIN        = 0,
 };
 
-int heads = 0;
-int sectors = 0;
-long long cylinders = 0;
-int cylinder_size = 0;         /* heads * sectors */
-long long actual_size = 0;     /* (in 512-byte sectors) - set using ioctl */
-                               /* explicitly given user values */
-int user_heads = 0, user_sectors = 0;
-long long user_cylinders = 0;
-                               /* kernel values; ignore the cylinders */
-int kern_heads = 0, kern_sectors = 0;
-                               /* partition-table derived values */
-int pt_heads = 0, pt_sectors = 0;
-
-
-static void
-set_hsc0(unsigned char *h, unsigned char *s, int *c, long long sector) {
-       *s = sector % sectors + 1;
-       sector /= sectors;
-       *h = sector % heads;
-       sector /= heads;
-       *c = sector;
-}
+static struct cfdisk_menudesc *menus[] = {
+       [CFDISK_MENU_MAIN] = menu_main
+};
 
-static void
-set_hsc(unsigned char *h, unsigned char *s, unsigned char *c,
-       long long sector) {
-       int cc;
+struct cfdisk {
+       struct fdisk_context    *cxt;   /* libfdisk context */
+       struct fdisk_table      *table; /* partition table */
 
-       if (sector >= 1024*cylinder_size)
-               sector = 1024*cylinder_size - 1;
-       set_hsc0(h, s, &cc, sector);
-       *c = cc & 0xFF;
-       *s |= (cc >> 2) & 0xC0;
-}
+       struct cfdisk_menu      *menu;  /* the current menu */ 
+       size_t                  menu_idx;
 
-static void
-set_hsc_begin(struct partition *p, long long sector) {
-       set_hsc(& p->head, & p->sector, & p->cyl, sector);
-}
+       int     *cols;          /* output columns */
+       size_t  ncols;          /* number of columns */
 
-static void
-set_hsc_end(struct partition *p, long long sector) {
-       set_hsc(& p->end_head, & p->end_sector, & p->end_cyl, sector);
-}
+       char    *linesbuf;      /* table as string */
+       size_t  linesbufsz;     /* size of the tb_buf */
 
-#define is_extended(x) ((x) == DOS_EXTENDED || (x) == WIN98_EXTENDED || \
-                        (x) == LINUX_EXTENDED)
-
-/* start_sect and nr_sects are stored little endian on all machines */
-/* moreover, they are not aligned correctly */
-static void
-store4_little_endian(unsigned char *cp, unsigned int val) {
-       cp[0] = (val & 0xff);
-       cp[1] = ((val >> 8) & 0xff);
-       cp[2] = ((val >> 16) & 0xff);
-       cp[3] = ((val >> 24) & 0xff);
-}
+       char    **lines;        /* array with lines */
+       size_t  nlines;         /* number of lines */
+       size_t  lines_idx;              /* current line <0..N>, exclude header */
 
-static unsigned int
-read4_little_endian(unsigned char *cp) {
-       return (unsigned int)(cp[0]) + ((unsigned int)(cp[1]) << 8)
-               + ((unsigned int)(cp[2]) << 16)
-               + ((unsigned int)(cp[3]) << 24);
-}
+       unsigned int    ui_enabled : 1;
+};
 
-static void
-set_start_sect(struct partition *p, unsigned int start_sect) {
-       store4_little_endian(p->start4, start_sect);
-}
+static int cols_init(struct cfdisk *cf)
+{
+       assert(cf);
 
-static unsigned int
-get_start_sect(struct partition *p) {
-       return read4_little_endian(p->start4);
-}
+       free(cf->cols);
+       cf->cols = NULL;
+       cf->ncols = 0;
 
-static void
-set_nr_sects(struct partition *p, unsigned int nr_sects) {
-       store4_little_endian(p->size4, nr_sects);
+       return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols);
 }
 
-static unsigned int
-get_nr_sects(struct partition *p) {
-       return read4_little_endian(p->size4);
-}
+/* It would be possible to use fdisk_table_to_string(), but we want some
+ * extension to the output format, so let's do it without libfdisk
+ */
+static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
+{
+       struct fdisk_partition *pa;
+       const struct fdisk_column *col;
+       struct fdisk_label *lb;
+       struct fdisk_iter *itr = NULL;
+       struct tt *tt = NULL;
+       char *res = NULL;
+       size_t i;
+
+       DBG(FRONTEND, dbgprint("table: convert to string"));
+
+       assert(cf);
+       assert(cf->cxt);
+       assert(cf->cols);
+       assert(tb);
+
+       lb = fdisk_context_get_label(cf->cxt, NULL);
+       assert(lb);
+
+       tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX);
+       if (!tt)
+               goto done;
+       itr = fdisk_new_iter(FDISK_ITER_FORWARD);
+       if (!itr)
+               goto done;
 
-#define ALIGNMENT 2
-typedef union {
-    struct {
-       unsigned char align[ALIGNMENT];
-       unsigned char b[SECTOR_SIZE];
-    } c;
-    struct {
-       unsigned char align[ALIGNMENT];
-       unsigned char buffer[0x1BE];
-       struct partition part[4];
-       unsigned char magicflag[2];
-    } p;
-} partition_table;
-
-typedef struct {
-    long long first_sector;    /* first sector in partition */
-    long long last_sector;     /* last sector in partition */
-    long offset;               /* offset from first sector to start of data */
-    int flags;         /* active == 0x80 */
-    int id;            /* filesystem type */
-    int num;           /* number of partition -- primary vs. logical */
-#define LABELSZ 16
-    char volume_label[LABELSZ+1];
-#define OSTYPESZ 8
-    char ostype[OSTYPESZ+1];
-#define FSTYPESZ 12
-    char fstype[FSTYPESZ+1];
-} partition_info;
-
-char *disk_device = DEFAULT_DEVICE;
-int fd;
-int changed = FALSE;
-int opened = FALSE;
-int opentype;
-int curses_started = 0;
-
-partition_info p_info[MAXIMUM_PARTS];
-partition_info ext_info;
-int num_parts = 0;
-
-int logical = 0;
-long long logical_sectors[MAXIMUM_PARTS];
-
-#ifdef HAVE_SIGHANDLER_T
-sighandler_t old_SIGINT, old_SIGTERM;
-#else
-void (* old_SIGINT)(int), (* old_SIGTERM)(int);
-#endif
+       /* headers */
+       for (i = 0; i < cf->ncols; i++) {
+               col = fdisk_label_get_column(lb, cf->cols[i]);
+               if (col)
+                       tt_define_column(tt, col->name,
+                                            col->width,
+                                            col->tt_flags);
+       }
+
+       /* data */
+       while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
+               struct tt_line *ln = tt_add_line(tt, NULL);
+               if (!ln)
+                       goto done;
+               for (i = 0; i < cf->ncols; i++) {
+                       char *cdata = NULL;
+
+                       col = fdisk_label_get_column(lb, cf->cols[i]);
+                       if (!col)
+                               continue;
+                       if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata))
+                               continue;
+                       tt_line_set_data(ln, i, cdata);
+               }
+       }
 
-int arrow_cursor = FALSE;
-int display_units = MEGABYTES;
-int zero_table = FALSE;
-int use_partition_table_geometry = FALSE;
-int print_only = 0;
-
-/* Curses screen information */
-int cur_part = 0;
-int warning_last_time = FALSE;
-int defined = FALSE;
-int COLUMNS = 80;
-int NUM_ON_SCREEN = 1;
-
-/* Y coordinates */
-int HEADER_START = 0;
-int DISK_TABLE_START = 6;
-int WARNING_START = 23;
-int COMMAND_LINE_Y = 21;
-
-/* X coordinates */
-int NAME_START = 4;
-int FLAGS_START = 16;
-int PTYPE_START = 28;
-int FSTYPE_START = 38;
-int LABEL_START = 54;
-int SIZE_START = 68;
-int COMMAND_LINE_X = 5;
-
-static void die_x(int ret);
-static void draw_screen(void);
-
-/* Guaranteed alloc */
-static void *
-xmalloc (size_t size) {
-     void *t;
-
-     if (size == 0)
-          return NULL;
-
-     t = malloc (size);
-     if (t == NULL) {
-          fprintf (stderr, _("%s: Out of memory!\n"), "cfdisk");
-         die_x(1);
-     }
-     return t;
-}
+       if (!tt_is_empty(tt)) {
+               tt_set_termreduce(tt, ARROW_CURSOR_WIDTH);
+               tt_print_table_to_string(tt, &res);
+       }
+done:
+       tt_free_table(tt);
+       fdisk_free_iter(itr);
 
-/* Some libc's have their own basename() */
-static char *
-my_basename(char *devname) {
-    char *s = strrchr(devname, '/');
-    return s ? s+1 : devname;
+       return res;
 }
 
-static char *
-partition_type_name(unsigned char type) {
-    struct systypes *s = i386_sys_types;
 
-    while(s->name && s->type != type)
-           s++;
-    return s->name;
-}
+static int lines_refresh_buffer(struct cfdisk *cf)
+{
+       int rc;
+       char *p;
+       size_t i;
 
-static char *
-partition_type_text(int i) {
-    if (p_info[i].id == UNUSABLE)
-        return _("Unusable");
-    else if (p_info[i].id == FREE_SPACE)
-        return _("Free Space");
-    else if (*p_info[i].fstype)
-        return p_info[i].fstype;
-    else
-        return _(partition_type_name(p_info[i].id));
-}
+       assert(cf);
 
-static void
-fdexit(int ret) {
-    if (opened) {
-       if (changed) {
-           if (close_fd(fd) != 0) {
-               fprintf(stderr, _("write failed\n"));
-               exit(2);
-           }
-       } else
-           close(fd);
-    }
-    if (changed) {
-       fprintf(stderr, _("Disk has been changed.\n"));
-#if 0
-       fprintf(stderr, _("Reboot the system to ensure the partition "
-                       "table is correctly updated.\n"));
-#endif
+       DBG(FRONTEND, dbgprint("refresing buffer"));
 
-       fprintf( stderr, _("\nWARNING: If you have created or modified any\n"
-                        "DOS 6.x partitions, please see the cfdisk manual\n"
-                        "page for additional information.\n") );
-    }
+       free(cf->linesbuf);
+       free(cf->lines);
+       cf->linesbuf = NULL;
+       cf->linesbufsz = 0;
+       cf->lines = NULL;
+       cf->nlines = 0;
 
-    exit(ret);
-}
+       fdisk_unref_table(cf->table);
+       fdisk_context_enable_freespace(cf->cxt, 1);
 
-/*
- * Note that @len is size of @str buffer.
- *
- * Returns number of read bytes (without \0).
- */
-static int
-get_string(char *str, int len, char *def) {
-    size_t cells = 0;
-    ssize_t i = 0;
-    int x, y;
-    int use_def = FALSE;
-    wint_t c;
-
-    getyx(stdscr, y, x);
-    clrtoeol();
-
-    str[i] = 0;
-
-    if (def != NULL) {
-       mvaddstr(y, x, def);
-       move(y, x);
-       use_def = TRUE;
-    }
-
-    refresh();
-
-    while (1) {
-#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
-    defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
-       if (get_wch(&c) == ERR) {
-#else
-       if ((c = getch()) == ERR) {
-#endif
-               if (!isatty(STDIN_FILENO))
-                       exit(2);
-               else
-                       break;
-       }
-       if (c == '\r' || c == '\n' || c == KEY_ENTER)
-               break;
+       rc = fdisk_get_table(cf->cxt, &cf->table);
+       if (rc)
+               return rc;
 
-       switch (c) {
-       case ESC:
-           move(y, x);
-           clrtoeol();
-           refresh();
-           return GS_ESCAPE;
-       case DEL:
-       case '\b':
-       case KEY_BACKSPACE:
-           if (i > 0) {
-               cells--;
-               i = mbs_truncate(str, &cells);
-               if (i < 0)
-                       return GS_ESCAPE;
-               mvaddch(y, x + cells, ' ');
-               move(y, x + cells);
-           } else if (use_def) {
-               clrtoeol();
-               use_def = FALSE;
-           } else
-               putchar(BELL);
-           break;
-       default:
-#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
-           if (i + 1 < len && iswprint(c)) {
-               wchar_t wc = (wchar_t) c;
-               char s[MB_CUR_MAX + 1];
-               int  sz = wctomb(s, wc);
-
-               if (sz > 0 && sz + i < len) {
-                       s[sz] = '\0';
-                       mvaddnstr(y, x + cells, s, sz);
-                       if (use_def) {
-                           clrtoeol();
-                           use_def = FALSE;
-                       }
-                       memcpy(str + i, s, sz);
-                       i += sz;
-                       str[i] = '\0';
-                       cells += wcwidth(wc);
-               } else
-                       putchar(BELL);
-           }
-#else
-           if (i + 1 < len && isprint(c)) {
-               mvaddch(y, x + cells, c);
-               if (use_def) {
-                   clrtoeol();
-                   use_def = FALSE;
-               }
-               str[i++] = c;
-               str[i] = 0;
-               cells++;
-           }
-#endif
-           else
-               putchar(BELL);
-       }
-       refresh();
-    }
+       cf->linesbuf = table_to_string(cf, cf->table);
+       if (!cf->linesbuf)
+               return -ENOMEM;
 
-    if (use_def)
-       return GS_DEFAULT;
-    else
-       return i;
-}
-
-static void
-clear_warning(void) {
-    int i;
+       cf->linesbufsz = strlen(cf->linesbuf);
+       cf->nlines = fdisk_table_get_nents(cf->table) + 1;      /* 1 for header line */
 
-    if (!curses_started || !warning_last_time)
-       return;
+       cf->lines = calloc(cf->nlines, sizeof(char *));
+       if (!cf->lines)
+               return -ENOMEM;
 
-    move(WARNING_START,0);
-    for (i = 0; i < COLS; i++)
-       addch(' ');
+       for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
+               cf->lines[i] = p;
+               p = strchr(p, '\n');
+               if (p) {
+                       *p = '\0';
+                       p++;
+               }
+       }
 
-    warning_last_time = FALSE;
+       return 0;
 }
 
-static void
-print_warning(char *s) {
-    if (!curses_started) {
-        fprintf(stderr, "%s\n", s);
-    } else {
-       mvaddstr(WARNING_START, (COLS-strlen(s))/2, s);
-       putchar(BELL); /* CTRL-G */
+static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
+                   void *data __attribute__((__unused__)))
+{
+       int rc = 0;
 
-       warning_last_time = TRUE;
-    }
-}
+       assert(cxt);
+       assert(ask);
 
-static void
-fatal(char *s, int ret) {
-    char *err1 = _("FATAL ERROR");
-    char *err2 = _("Press any key to exit cfdisk");
-
-    if (curses_started) {
-        char *str = xmalloc(strlen(s) + strlen(err1) + strlen(err2) + 10);
-
-        sprintf(str, "%s: %s", err1, s);
-        if (strlen(str) > (size_t) COLS)
-            str[COLS] = 0;
-        mvaddstr(WARNING_START, (COLS-strlen(str))/2, str);
-        sprintf(str, "%s", err2);
-        if (strlen(str) > (size_t) COLS)
-            str[COLS] = 0;
-        mvaddstr(WARNING_START+1, (COLS-strlen(str))/2, str);
-        putchar(BELL); /* CTRL-G */
-        refresh();
-        (void)getch();
-        die_x(ret);
-    } else {
-        fprintf(stderr, "%s: %s\n", err1, s);
-        exit(ret);
-    }
+       switch(fdisk_ask_get_type(ask)) {
+       case FDISK_ASKTYPE_INFO:
+               fputs(fdisk_ask_print_get_mesg(ask), stdout);
+               fputc('\n', stdout);
+               break;
+       case FDISK_ASKTYPE_WARNX:
+               fputs(fdisk_ask_print_get_mesg(ask), stderr);
+               fputc('\n', stderr);
+               break;
+       case FDISK_ASKTYPE_WARN:
+               fputs(fdisk_ask_print_get_mesg(ask), stderr);
+               errno = fdisk_ask_print_get_errno(ask);
+               fprintf(stderr, ": %m\n");
+               break;
+       default:
+               warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
+               return -EINVAL;
+       }
+       return rc;
 }
 
-static void
-die(int dummy __attribute__((__unused__))) {
-    die_x(0);
-}
 
-static void
-die_x(int ret) {
-    signal(SIGINT, old_SIGINT);
-    signal(SIGTERM, old_SIGTERM);
+static int ui_end(struct cfdisk *cf)
+{
+       if (cf && !cf->ui_enabled)
+               return -EINVAL;
+
 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
-    SLsmg_gotorc(LINES-1, 0);
-    SLsmg_refresh();
+       SLsmg_gotorc(LINES - 1, 0);
+       SLsmg_refresh();
 #else
-    mvcur(0, COLS-1, LINES-1, 0);
+       mvcur(0, COLS - 1, LINES-1, 0);
 #endif
-    nl();
-    endwin();
-    printf("\n");
-    fdexit(ret);
+       nl();
+       endwin();
+       printf("\n");
+       return 0;
 }
 
-static void
-read_sector(unsigned char *buffer, long long sect_num) {
-    if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
-       fatal(_("Cannot seek on disk drive"), 2);
-    if (read(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
-       fatal(_("Cannot read disk drive"), 2);
-}
-
-static void
-write_sector(unsigned char *buffer, long long sect_num) {
-    if (lseek(fd, sect_num*SECTOR_SIZE, SEEK_SET) < 0)
-       fatal(_("Cannot seek on disk drive"), 2);
-    if (write(fd, buffer, SECTOR_SIZE) != SECTOR_SIZE)
-       fatal(_("Cannot write disk drive"), 2);
-}
-
-#ifdef HAVE_LIBBLKID
-static void
-get_fsinfo(int i)
+static void ui_print_center(int line, const char *fmt, ...)
 {
-       blkid_probe pr;
-       blkid_loff_t offset, size;
-       const char *data;
-
-       offset = (p_info[i].first_sector + p_info[i].offset) * SECTOR_SIZE;
-       size = (p_info[i].last_sector - p_info[i].first_sector + 1) * SECTOR_SIZE;
-       pr = blkid_new_probe();
-       if (!pr)
-               return;
-       blkid_probe_enable_superblocks(pr, 1);
-       blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_LABEL |
-                                             BLKID_SUBLKS_TYPE);
-       if (blkid_probe_set_device(pr, fd, offset, size))
-               goto done;
-       if (blkid_do_safeprobe(pr))
-               goto done;
+       size_t width;
+       va_list ap;
+       char *buf = NULL;
 
-       if (!blkid_probe_lookup_value(pr, "TYPE", &data, 0))
-               strncpy(p_info[i].fstype, data, FSTYPESZ);
+       move(line, 0);
+       clrtoeol();
 
-       if (!blkid_probe_lookup_value(pr, "LABEL", &data, 0))
-               strncpy(p_info[i].volume_label, data, LABELSZ);
-done:
-       blkid_free_probe(pr);
-}
-#endif
+       va_start(ap, fmt);
+       xvasprintf(&buf, fmt, ap);
+       va_end(ap);
 
-static void
-check_part_info(void) {
-    int i, pri = 0, log = 0;
-
-    for (i = 0; i < num_parts; i++)
-       if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
-           pri++;
-       else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
-           log++;
-    if (is_extended(ext_info.id)) {
-       if (log > 0)
-           pri++;
-       else {
-           ext_info.first_sector = 0;
-           ext_info.last_sector = 0;
-           ext_info.offset = 0;
-           ext_info.flags = 0;
-           ext_info.id = FREE_SPACE;
-           ext_info.num = PRIMARY;
-       }
-    }
-
-    if (pri >= 4) {
-       for (i = 0; i < num_parts; i++)
-           if (p_info[i].id == FREE_SPACE || p_info[i].id == UNUSABLE) {
-               if (is_extended(ext_info.id)) {
-                   if (p_info[i].first_sector >= ext_info.first_sector &&
-                       p_info[i].last_sector <= ext_info.last_sector) {
-                       p_info[i].id = FREE_SPACE;
-                       p_info[i].num = LOGICAL;
-                   } else if (i > 0 &&
-                              p_info[i-1].first_sector >=
-                              ext_info.first_sector &&
-                              p_info[i-1].last_sector <=
-                              ext_info.last_sector) {
-                       p_info[i].id = FREE_SPACE;
-                       p_info[i].num = LOGICAL;
-                   } else if (i < num_parts-1 &&
-                              p_info[i+1].first_sector >=
-                              ext_info.first_sector &&
-                              p_info[i+1].last_sector <=
-                              ext_info.last_sector) {
-                       p_info[i].id = FREE_SPACE;
-                       p_info[i].num = LOGICAL;
-                   } else
-                       p_info[i].id = UNUSABLE;
-               } else /* if (!is_extended(ext_info.id)) */
-                   p_info[i].id = UNUSABLE;
-           } else /* if (p_info[i].id > 0) */
-               while (0); /* Leave these alone */
-    } else { /* if (pri < 4) */
-       for (i = 0; i < num_parts; i++) {
-           if (p_info[i].id == UNUSABLE)
-               p_info[i].id = FREE_SPACE;
-           if (p_info[i].id == FREE_SPACE) {
-               if (is_extended(ext_info.id)) {
-                   if (p_info[i].first_sector >= ext_info.first_sector &&
-                       p_info[i].last_sector <= ext_info.last_sector)
-                       p_info[i].num = LOGICAL;
-                   else if (i > 0 &&
-                            p_info[i-1].first_sector >=
-                            ext_info.first_sector &&
-                            p_info[i-1].last_sector <=
-                            ext_info.last_sector)
-                       p_info[i].num = PRI_OR_LOG;
-                   else if (i < num_parts-1 &&
-                            p_info[i+1].first_sector >=
-                            ext_info.first_sector &&
-                            p_info[i+1].last_sector <=
-                            ext_info.last_sector)
-                       p_info[i].num = PRI_OR_LOG;
-                   else
-                       p_info[i].num = PRIMARY;
-               } else /* if (!is_extended(ext_info.id)) */
-                   p_info[i].num = PRI_OR_LOG;
-           } else /* if (p_info[i].id > 0) */
-               while (0); /* Leave these alone */
-       }
-    }
+       width = strlen(buf);                    /* TODO: count cells! */
+       mvaddstr(line, (COLS - width) / 2, buf);
+       free(buf);
 }
 
-static void
-remove_part(int i) {
-    int p;
 
-    for (p = i; p < num_parts; p++)
-       p_info[p] = p_info[p+1];
-
-    num_parts--;
-    if (cur_part == num_parts)
-       cur_part--;
+static void die_on_signal(int dummy __attribute__((__unused__)))
+{
+       ui_end(NULL);
+       exit(EXIT_FAILURE);
 }
 
-static void
-insert_empty_part(int i, long long first, long long last) {
-    int p;
-
-    for (p = num_parts; p > i; p--)
-        p_info[p] = p_info[p-1];
+static void menu_update_ignore(struct cfdisk *cf)
+{
+       char *ignore = NULL;
+       struct cfdisk_menu *m;
+       struct cfdisk_menudesc *d;
 
-    p_info[i].first_sector = first;
-    p_info[i].last_sector = last;
-    p_info[i].offset = 0;
-    p_info[i].flags = 0;
-    p_info[i].id = FREE_SPACE;
-    p_info[i].num = PRI_OR_LOG;
-    p_info[i].volume_label[0] = 0;
-    p_info[i].fstype[0] = 0;
-    p_info[i].ostype[0] = 0;
+       assert(cf);
 
-    num_parts++;
-}
+       m = cf->menu;
+       DBG(FRONTEND, dbgprint("menu: update menu ignored keys"));
 
-static void
-del_part(int i) {
-    int num = p_info[i].num;
-
-    if (i > 0 && (p_info[i-1].id == FREE_SPACE ||
-                 p_info[i-1].id == UNUSABLE)) {
-       /* Merge with previous partition */
-       p_info[i-1].last_sector = p_info[i].last_sector;
-       remove_part(i--);
-    }
-
-    if (i < num_parts - 1 && (p_info[i+1].id == FREE_SPACE ||
-                             p_info[i+1].id == UNUSABLE)) {
-       /* Merge with next partition */
-       p_info[i+1].first_sector = p_info[i].first_sector;
-       remove_part(i);
-    }
-
-    if (i > 0)
-       p_info[i].first_sector = p_info[i-1].last_sector + 1;
-    else
-       p_info[i].first_sector = 0;
-
-    if (i < num_parts - 1)
-       p_info[i].last_sector = p_info[i+1].first_sector - 1;
-    else
-       p_info[i].last_sector = actual_size - 1;
-
-    p_info[i].offset = 0;
-    p_info[i].flags = 0;
-    p_info[i].id = FREE_SPACE;
-    p_info[i].num = PRI_OR_LOG;
-
-    if (IS_LOGICAL(num)) {
-       /* We have a logical partition --> shrink the extended partition
-        * if (1) this is the first logical drive, or (2) this is the
-        * last logical drive; and if there are any other logical drives
-        * then renumber the ones after "num".
-        */
-       if (i == 0 || (i > 0 && IS_PRIMARY(p_info[i-1].num))) {
-           ext_info.first_sector = p_info[i].last_sector + 1;
-           ext_info.offset = 0;
+       switch (m->id) {
+       case CFDISK_MENU_MAIN:
+               break;
        }
-       if (i == num_parts-1 ||
-           (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)))
-           ext_info.last_sector = p_info[i].first_sector - 1;
-       for (i = 0; i < num_parts; i++)
-           if (p_info[i].num > num)
-               p_info[i].num--;
-    }
-
-    /* Clean up the rest of the partitions */
-    check_part_info();
-}
 
-static int
-add_part(int num, int id, int flags, long long first, long long last,
-        long long offset, int want_label, char **errmsg) {
-    int i, pri = 0, log = 0;
-
-    if (num_parts == MAXIMUM_PARTS) {
-       *errmsg = _("Too many partitions");
-       return -1;
-    }
-
-    if (first < 0) {
-       *errmsg = _("Partition begins before sector 0");
-       return -1;
-    }
-
-    if (last < 0) {
-       *errmsg = _("Partition ends before sector 0");
-       return -1;
-    }
-
-    if (first >= actual_size) {
-       *errmsg = _("Partition begins after end-of-disk");
-       return -1;
-    }
-
-    if (last >= actual_size) {
-       *errmsg = _("Partition ends after end-of-disk");
-       return -1;
-    }
-
-    for (i = 0; i < num_parts; i++) {
-       if (p_info[i].id > 0 && IS_PRIMARY(p_info[i].num))
-           pri++;
-       else if (p_info[i].id > 0 && IS_LOGICAL(p_info[i].num))
-           log++;
-    }
-    if (is_extended(ext_info.id) && log > 0)
-       pri++;
-
-    if (IS_PRIMARY(num)) {
-       if (pri >= 4) {
-           return -1;          /* no room for more */
-       } else
-           pri++;
-    }
-
-    for (i = 0; i < num_parts && p_info[i].last_sector < first; i++);
-
-    if (i < num_parts && p_info[i].id != FREE_SPACE) {
-        if (last < p_info[i].first_sector)
-             *errmsg = _("logical partitions not in disk order");
-        else if (first + offset <= p_info[i].last_sector &&
-                 p_info[i].first_sector + p_info[i].offset <= last)
-             *errmsg = _("logical partitions overlap");
-        else
-             /* the enlarged logical partition starts at the
-                partition table sector that defines it */
-             *errmsg = _("enlarged logical partitions overlap");
-        return -1;
-    }
-
-    if (i == num_parts || last > p_info[i].last_sector) {
-       return -1;
-    }
-
-    if (is_extended(id)) {
-       if (ext_info.id != FREE_SPACE) {
-           return -1;          /* second extended */
-       }
-       else if (IS_PRIMARY(num)) {
-           ext_info.first_sector = first;
-           ext_info.last_sector = last;
-           ext_info.offset = offset;
-           ext_info.flags = flags;
-           ext_info.id = id;
-           ext_info.num = num;
-           ext_info.volume_label[0] = 0;
-           ext_info.fstype[0] = 0;
-           ext_info.ostype[0] = 0;
-           return 0;
-       } else {
-           return -1;          /* explicit extended logical */
+       /* return if no change */
+       if (   (!m->ignore && (!ignore || !*ignore))
+           || (m->ignore && ignore && strcmp(m->ignore, ignore) == 0)) {
+                   free(ignore);
+                   return;
        }
-    }
 
-    if (IS_LOGICAL(num)) {
-       if (!is_extended(ext_info.id)) {
-           print_warning(_("!!!! Internal error creating logical "
-                         "drive with no extended partition !!!!"));
-       } else {
-           /* We might have a logical partition outside of the extended
-            * partition's range --> we have to extend the extended
-            * partition's range to encompass this new partition, but we
-            * must make sure that there are no primary partitions between
-            * it and the closest logical drive in extended partition.
-            */
-           if (first < ext_info.first_sector) {
-               if (i < num_parts-1 && IS_PRIMARY(p_info[i+1].num)) {
-                   print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
-                   return -1;
-               } else {
-                   if (first == 0) {
-                       ext_info.first_sector = 0;
-                       ext_info.offset = first = offset;
-                   } else {
-                       ext_info.first_sector = first;
-                   }
-               }
-           } else if (last > ext_info.last_sector) {
-               if (i > 0 && IS_PRIMARY(p_info[i-1].num)) {
-                   print_warning(_("Cannot create logical drive here -- would create two extended partitions"));
-                   return -1;
-               } else {
-                   ext_info.last_sector = last;
-               }
-           }
-       }
-    }
-
-    if (first != p_info[i].first_sector &&
-       !(IS_LOGICAL(num) && first == offset)) {
-       insert_empty_part(i, p_info[i].first_sector, first-1);
-       i++;
-    }
-
-    if (last != p_info[i].last_sector)
-       insert_empty_part(i+1, last+1, p_info[i].last_sector);
-
-    p_info[i].first_sector = first;
-    p_info[i].last_sector = last;
-    p_info[i].offset = offset;
-    p_info[i].flags = flags;
-    p_info[i].id = id;
-    p_info[i].num = num;
-    p_info[i].volume_label[0] = 0;
-    p_info[i].fstype[0] = 0;
-    p_info[i].ostype[0] = 0;
-
-#ifdef HAVE_LIBBLKID
-    if (want_label)
-        get_fsinfo(i);
-#endif
-    check_part_info();
+       free(m->ignore);
+       m->ignore = ignore;
+       m->nitems = 0;
 
-    return 0;
+       for (d = m->desc; d->name; d++) {
+               if (m->ignore && strchr(m->ignore, d->key))
+                       m->nitems++;
+       }
 }
 
-static int
-find_primary(void) {
-    int num = 0, cur = 0;
-
-    while (cur < num_parts && IS_PRIMARY(num))
-       if ((p_info[cur].id > 0 && p_info[cur].num == num) ||
-           (is_extended(ext_info.id) && ext_info.num == num)) {
-           num++;
-           cur = 0;
-       } else
-           cur++;
+static struct cfdisk_menu *menu_push(struct cfdisk *cf, size_t id)
+{
+       struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
+       struct cfdisk_menudesc *d;
 
-    if (!IS_PRIMARY(num))
-       return -1;
-    else
-       return num;
-}
+       assert(cf);
+       assert(id < ARRAY_SIZE(menus));
 
-static int
-find_logical(int i) {
-    int num = -1;
-    int j;
+       DBG(FRONTEND, dbgprint("menu: new menu"));
 
-    for (j = i; j < num_parts && num == -1; j++)
-       if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
-           num = p_info[j].num;
+       m->prev = cf->menu;
+       m->id = id;
+       m->desc = menus[id];
 
-    if (num == -1) {
-       num = 4;
-       for (j = 0; j < num_parts; j++)
-           if (p_info[j].id > 0 && p_info[j].num == num)
-               num++;
-    }
+       for (d = m->desc; d->name; d++) {
+               const char *name = _(d->name);
+               size_t len = strlen(name);      /* TODO: we care about cells! */
+               if (len > m->width)
+                       m->width = len;
+               m->nitems++;
+       }
 
-    return num;
+       cf->menu = m;
+       return m;
 }
 
-/*
- * Command menu support by Janne Kukonlehto <jtklehto@phoenix.oulu.fi>
- * September 1994
- */
-
-/* Constants for menuType parameter of menuSelect function */
-#define MENU_ACCEPT_OTHERS 4
-#define MENU_BUTTON 8
-/* Miscellenous constants */
-#define MENU_SPACING 2
-#define MENU_MAX_ITEMS 256 /* for simpleMenu function */
-
-struct MenuItem
+static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
 {
-    int key; /* Keyboard shortcut; if zero, then there is no more items in the menu item table */
-    char *name; /* Item name, should be eight characters with current implementation */
-    char *desc; /* Item description to be printed when item is selected */
-};
-
-/*
- * Actual function which prints the button bar and highlights the active button
- * Should not be called directly. Call function menuSelect instead.
- */
+       struct cfdisk_menu *m = NULL;
 
-static int
-menuUpdate( int y, int x, struct MenuItem *menuItems, int itemLength,
-           char *available, int menuType, int current ) {
-    int i, lmargin = x;
-    char *mcd;
+       assert(cf);
 
-    /* Print available buttons */
-    move( y, x ); clrtoeol();
+       DBG(FRONTEND, dbgprint("menu: rem menu"));
 
-    for( i = 0; menuItems[i].key; i++ ) {
-        char buff[20];
-        int lenName;
-       const char *mi;
-
-        /* Search next available button */
-        while( menuItems[i].key && !strchr(available, menuItems[i].key) )
-            i++;
-
-        if( !menuItems[i].key ) break; /* No more menu items */
-
-        /* If selected item is not available and we have bypassed it,
-          make current item selected */
-        if( current < i && menuItems[current].key < 0 ) current = i;
-
-        /* If current item is selected, highlight it */
-        if( current == i ) /*attron( A_REVERSE )*/ standout ();
-
-        /* Print item */
-       /* Because of a bug in gettext() we must not translate empty strings */
-       if (menuItems[i].name[0])
-               mi = _(menuItems[i].name);
-       else
-               mi = "";
-        lenName = strlen( mi );
-#if 0
-        if(lenName > itemLength || lenName >= sizeof(buff))
-            print_warning(_("Menu item too long. Menu may look odd."));
-#endif
-       if ((size_t) lenName >= sizeof(buff)) { /* truncate ridiculously long string */
-           xstrncpy(buff, mi, sizeof(buff));
-       } else if (lenName >= itemLength) {
-            snprintf(buff, sizeof(buff),
-                    (menuType & MENU_BUTTON) ? "[%s]" : "%s", mi);
-       } else {
-            snprintf(buff, sizeof(buff),
-                    (menuType & MENU_BUTTON) ? "[%*s%-*s]" : "%*s%-*s",
-                    (itemLength - lenName) / 2, "",
-                    (itemLength - lenName + 1) / 2 + lenName, mi);
+       if (cf->menu) {
+               m = cf->menu->prev;
+               free(cf->menu->ignore);
+               free(cf->menu);
        }
-        mvaddstr( y, x, buff );
-
-        /* Lowlight after selected item */
-        if( current == i ) /*attroff( A_REVERSE )*/ standend ();
-
-        /* Calculate position for the next item */
-            x += itemLength + MENU_SPACING;
-            if( menuType & MENU_BUTTON ) x += 2;
-            if( x > COLUMNS - lmargin - 12 )
-            {
-                x = lmargin;
-                y ++ ;
-            }
-    }
-
-    /* Print the description of selected item */
-    mcd = _(menuItems[current].desc);
-    mvaddstr( WARNING_START + 1, (COLUMNS - strlen( mcd )) / 2, mcd );
-    return y;
+       cf->menu = m;
+       return cf->menu;
 }
 
-/* This function takes a list of menu items, lets the user choose one *
- * and returns the keyboard shortcut value of the selected menu item  */
-
-static int
-menuSelect( int y, int x, struct MenuItem *menuItems, int itemLength,
-           char *available, int menuType, int menuDefault ) {
-    int i, ylast = y, key = 0, current = menuDefault;
-
-    /* Make sure that the current is one of the available items */
-    while( !strchr(available, menuItems[current].key) ) {
-        current ++ ;
-        if( !menuItems[current].key ) current = 0;
-    }
-
-    keypad(stdscr, TRUE);
-
-    /* Repeat until allowable choice has been made */
-    while( !key ) {
-        /* Display the menu and read a command */
-        ylast = menuUpdate( y, x, menuItems, itemLength, available,
-                           menuType, current );
-        refresh();
-        key = getch();
-
-       if (key == ERR)
-               if (!isatty(STDIN_FILENO))
-                       exit(2);
-
-        /* Clear out all prompts and such */
-        clear_warning();
-        for (i = y; i < ylast; i++) {
-            move(i, x);
-            clrtoeol();
-        }
-        move( WARNING_START + 1, 0 );
-        clrtoeol();
-
-       switch (key) {
-       case KEY_RIGHT:
-       case '\t':
-               /* Select next menu item */
-               do {
-                       current++;
-                       if (!menuItems[current].key)
-                               current = 0;
-               } while (!strchr(available, menuItems[current].key));
-               key = 0;
-               break;
-       case KEY_LEFT:
-#ifdef KEY_BTAB
-       case KEY_BTAB:  /* Back tab */
-#endif
-               /* Select previous menu item */
-               do {
-                       current--;
-                       if (current < 0) {
-                               while (menuItems[current + 1].key)
-                                       current++;
-                       }
-               } while (!strchr(available, menuItems[current].key));
-               key = 0;
-               break;
-       case KEY_ENTER:
-       case '\n':
-       case '\r':
-               /* Enter equals the keyboard shortcut of current menu item */
-               key = menuItems[current].key;
-               break;
-       }
 
-        /* Should all keys to be accepted? */
-        if( key && (menuType & MENU_ACCEPT_OTHERS) ) break;
-
-        /* Is pressed key among acceptable ones? */
-        if( key && (strchr(available, tolower(key)) || strchr(available, key)))
-           break;
-
-        /* The key has not been accepted so far -> let's reject it */
-        if (key) {
-            key = 0;
-            putchar( BELL );
-            print_warning(_("Illegal key"));
-        }
-    }
-
-    keypad(stdscr, FALSE);
-
-    /* Clear out prompts and such */
-    clear_warning();
-    for( i = y; i <= ylast; i ++ ) {
-        move( i, x );
-        clrtoeol();
-    }
-    move( WARNING_START + 1, 0 );
-    clrtoeol();
-    return key;
-}
+static int ui_init(struct cfdisk *cf)
+{
+       struct sigaction sa;
 
-/* A function which displays "Press a key to continue"                  *
- * and waits for a keypress.                                            *
- * Perhaps calling function menuSelect is a bit overkill but who cares? */
+       DBG(FRONTEND, dbgprint("ui: init"));
 
-static void
-menuContinue(void) {
-    static struct MenuItem menuContinueBtn[]=
-    {
-        { 'c', "", N_("Press a key to continue") },
-        { 0, NULL, NULL }
-    };
+       /* setup SIGCHLD handler */
+       sigemptyset(&sa.sa_mask);
+       sa.sa_flags = 0;
+       sa.sa_handler = die_on_signal;
+       sigaction(SIGINT, &sa, NULL);
+       sigaction(SIGTERM, &sa, NULL);
 
-    menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X,
-       menuContinueBtn, 0, "c", MENU_ACCEPT_OTHERS, 0 );
-}
+       cf->ui_enabled = 1;
+       initscr();
 
-/* Function menuSelect takes way too many parameters  *
- * Luckily, most of time we can do with this function */
-
-static int
-menuSimple(struct MenuItem *menuItems, int menuDefault) {
-    int i, j, itemLength = 0;
-    char available[MENU_MAX_ITEMS];
-
-    for(i = 0; menuItems[i].key; i++)
-    {
-        j = strlen( _(menuItems[i].name) );
-        if( j > itemLength ) itemLength = j;
-        available[i] = menuItems[i].key;
-    }
-    available[i] = 0;
-    return menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuItems, itemLength,
-        available, MENU_BUTTON, menuDefault);
-}
+       cbreak();
+       noecho();
+       nonl();
+       curs_set(0);
+       keypad(stdscr, TRUE);
 
-/* End of command menu support code */
-
-static void
-new_part(int i) {
-    char response[LINE_LENGTH], def[LINE_LENGTH];
-    char c;
-    long long first = p_info[i].first_sector;
-    long long last = p_info[i].last_sector;
-    long long offset = 0;
-    int flags = 0;
-    int id = LINUX;
-    int num = -1;
-    long long num_sects = last - first + 1;
-    int len, ext, j;
-    char *errmsg;
-    double sectors_per_MB = K*K / 512.0;
-
-    if (p_info[i].num == PRI_OR_LOG) {
-        static struct MenuItem menuPartType[]=
-        {
-            { 'p', N_("Primary"), N_("Create a new primary partition") },
-            { 'l', N_("Logical"), N_("Create a new logical partition") },
-            { ESC, N_("Cancel"), N_("Don't create a partition") },
-            { 0, NULL, NULL }
-        };
-
-       c = menuSimple( menuPartType, 0 );
-       if (toupper(c) == 'P')
-           num = find_primary();
-       else if (toupper(c) == 'L')
-           num = find_logical(i);
-       else
-           return;
-    } else if (p_info[i].num == PRIMARY)
-       num = find_primary();
-    else if (p_info[i].num == LOGICAL)
-       num = find_logical(i);
-    else
-       print_warning(_("!!! Internal error !!!"));
-
-    snprintf(def, sizeof(def), "%.2f", num_sects/sectors_per_MB);
-    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Size (in MB): "));
-    if ((len = get_string(response, LINE_LENGTH, def)) <= 0 &&
-       len != GS_DEFAULT)
-       return;
-    else if (len > 0) {
-#define num_cyls(bytes) (round_int(bytes/SECTOR_SIZE/cylinder_size))
-       for (j = 0;
-            j < len-1 && (isdigit(response[j]) || response[j] == '.');
-            j++);
-       if (toupper(response[j]) == 'K') {
-           num_sects = num_cyls(atof(response)*K)*cylinder_size;
-       } else if (toupper(response[j]) == 'M') {
-           num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
-       } else if (toupper(response[j]) == 'G') {
-           num_sects = num_cyls(atof(response)*K*K*K)*cylinder_size;
-       } else if (toupper(response[j]) == 'C') {
-           num_sects = round_int(atof(response))*cylinder_size;
-       } else if (toupper(response[j]) == 'S') {
-           num_sects = round_int(atof(response));
-       } else {
-           num_sects = num_cyls(atof(response)*K*K)*cylinder_size;
-       }
-    }
-
-    if (num_sects <= 0 ||
-       num_sects > p_info[i].last_sector - p_info[i].first_sector + 1)
-       return;
-
-    move( COMMAND_LINE_Y, COMMAND_LINE_X ); clrtoeol();
-    if (num_sects < p_info[i].last_sector - p_info[i].first_sector + 1) {
-       /* Determine where inside free space to put partition.
-        */
-       static struct MenuItem menuPlace[]=
-       {
-           { 'b', N_("Beginning"), N_("Add partition at beginning of free space") },
-           { 'e', N_("End"), N_("Add partition at end of free space") },
-           { ESC, N_("Cancel"), N_("Don't create a partition") },
-           { 0, NULL, NULL }
-       };
-       c = menuSimple( menuPlace, 0 );
-       if (toupper(c) == 'B')
-           last = first + num_sects - 1;
-       else if (toupper(c) == 'E')
-           first = last - num_sects + 1;
-       else
-           return;
-    }
-
-    if (IS_LOGICAL(num) && !is_extended(ext_info.id)) {
-       /* We want to add a logical partition, but need to create an
-        * extended partition first.
-        */
-       if ((ext = find_primary()) < 0) {
-           print_warning(_("No room to create the extended partition"));
-           return;
-       }
-       errmsg = 0;
-       if (add_part(ext, DOS_EXTENDED, 0, first, last,
-                    (first == 0 ? sectors : 0), 0, &errmsg) && errmsg)
-               print_warning(errmsg);
-       first = ext_info.first_sector + ext_info.offset;
-    }
-
-    /* increment number of all partitions past this one */
-    if (IS_LOGICAL(num)) {
-#if 0
-       /* original text - ok, but fails when partitions not in disk order */
-       for (j = i; j < num_parts; j++)
-           if (p_info[j].id > 0 && IS_LOGICAL(p_info[j].num))
-               p_info[j].num++;
-#else
-       /* always ok */
-       for (j = 0; j < num_parts; j++)
-           if (p_info[j].id > 0 && p_info[j].num >= num)
-               p_info[j].num++;
-#endif
-    }
-
-    /* Now we have a complete partition to ourselves */
-    if (first == 0 || IS_LOGICAL(num))
-       offset = sectors;
-
-    errmsg = 0;
-    if (add_part(num, id, flags, first, last, offset, 0, &errmsg) && errmsg)
-           print_warning(errmsg);
+       return 0;
 }
 
-static void
-get_kernel_geometry(void) {
-#ifdef HDIO_GETGEO
-    struct hd_geometry geometry;
+static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
+{
+       size_t len = cf->menu->width + 4 + MENU_PADDING;        /* item width */
+       size_t items = COLS / len;                              /* items per line */
 
-    if (!ioctl(fd, HDIO_GETGEO, &geometry)) {
-       kern_heads = geometry.heads;
-       kern_sectors = geometry.sectors;
-    }
-#endif
+       return MENU_START_LINE + ((idx / items));
 }
 
-static int
-said_yes(char answer) {
-       char reply[2];
+static int menuitem_get_column(struct cfdisk *cf, size_t idx)
+{
+       size_t len = cf->menu->width + 4 + MENU_PADDING;        /* item width */
+       size_t items = COLS / len;                              /* items per line */
+       size_t extra = items < cf->menu->nitems ?               /* extra space on line */
+                       COLS % len :                            /* - multi-line menu */
+                       COLS - (cf->menu->nitems * len);        /* - one line menu */
 
-       reply[0] = answer;
-       reply[1] = 0;
+       extra += MENU_PADDING;          /* add padding after last item to extra */
 
-       return (rpmatch(reply) == 1) ? 1 : 0;
+       if (idx < items)
+               return (idx * len) + (extra / 2);
+       return ((idx % items) * len) + (extra / 2);
 }
 
-static void
-get_partition_table_geometry(partition_table *bufp) {
-       struct partition *p;
-       int i,h,s,hh,ss;
-       int first = TRUE;
-       int bad = FALSE;
-
-       for (i=0; i<66; i++)
-               if (bufp->c.b[446+i])
-                       goto nonz;
-
-       /* zero table */
-       if (!curses_started) {
-               fatal(_("No partition table.\n"), 3);
-               return;
-       } else {
-               mvaddstr(WARNING_START, 0,
-                        _("No partition table. Starting with zero table."));
-               putchar(BELL);
-               refresh();
-               zero_table = TRUE;
-               return;
-       }
- nonz:
-       if (bufp->p.magicflag[0] != PART_TABLE_FLAG0 ||
-           bufp->p.magicflag[1] != PART_TABLE_FLAG1) {
-               if (!curses_started)
-                       fatal(_("Bad signature on partition table"), 3);
-
-               /* Matthew Wilcox */
-               mvaddstr(WARNING_START, 0,
-                        _("Unknown partition table type"));
-               mvaddstr(WARNING_START+1, 0,
-                        _("Do you wish to start with a zero table [y/N] ?"));
-               putchar(BELL);
-               refresh();
-               {
-                       int cont = getch();
-                       if (cont == EOF || !said_yes(cont))
-                               die_x(3);
-               }
-               zero_table = TRUE;
-               return;
-       }
-
-       hh = ss = 0;
-       for (i=0; i<4; i++) {
-               p = &(bufp->p.part[i]);
-               if (p->sys_ind != 0) {
-                       h = p->end_head + 1;
-                       s = (p->end_sector & 077);
-                       if (first) {
-                               hh = h;
-                               ss = s;
-                               first = FALSE;
-                       } else if (hh != h || ss != s)
-                               bad = TRUE;
-               }
-       }
+static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx)
+{
+       struct cfdisk_menudesc *d;
+       size_t i;
 
-       if (!first && !bad) {
-               pt_heads = hh;
-               pt_sectors = ss;
+       for (i = 0, d = cf->menu->desc; d->name; d++) {
+               if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
+                       continue;
+               if (i++ == idx)
+                       return d;
        }
-}
 
-static void
-decide_on_geometry(void) {
-    heads = (user_heads ? user_heads :
-            pt_heads ? pt_heads :
-            kern_heads ? kern_heads : 255);
-    sectors = (user_sectors ? user_sectors :
-              pt_sectors ? pt_sectors :
-              kern_sectors ? kern_sectors : 63);
-    cylinder_size = heads*sectors;
-    cylinders = actual_size/cylinder_size;
-    if (user_cylinders > 0)
-           cylinders = user_cylinders;
-
-    if (cylinder_size * cylinders > actual_size)
-           print_warning(_("You specified more cylinders than fit on disk"));
+       return NULL;
 }
 
-static void
-clear_p_info(void) {
-    num_parts = 1;
-    p_info[0].first_sector = 0;
-    p_info[0].last_sector = actual_size - 1;
-    p_info[0].offset = 0;
-    p_info[0].flags = 0;
-    p_info[0].id = FREE_SPACE;
-    p_info[0].num = PRI_OR_LOG;
-
-    ext_info.first_sector = 0;
-    ext_info.last_sector = 0;
-    ext_info.offset = 0;
-    ext_info.flags = 0;
-    ext_info.id = FREE_SPACE;
-    ext_info.num = PRIMARY;
+static void ui_draw_menuitem(struct cfdisk *cf,
+                            struct cfdisk_menudesc *d,
+                            size_t idx)
+{
+       char buf[80 * MB_CUR_MAX];
+       const char *name;
+       size_t width = cf->menu->width + 2;     /* 2 = blank around string */
+       int ln, cl;
+
+       name = _(d->name);
+       mbsalign(name, buf, sizeof(buf), &width, MBS_ALIGN_CENTER, 0);
+
+       ln = menuitem_get_line(cf, idx);
+       cl = menuitem_get_column(cf, idx);
+
+       DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
+                       cl, ln, buf));
+
+       if (cf->menu_idx == idx) {
+               standout();
+               mvprintw(ln, cl, "[%s]", buf);
+               standend();
+               if (d->desc)
+                       ui_print_center(LINES - 1, d->desc);
+       } else
+               mvprintw(ln, cl, "[%s]", buf);
 }
 
-static void
-fill_p_info(void) {
-    int pn, i;
-    long long bs, bsz;
-    unsigned long long llsectors;
-    struct partition *p;
-    partition_table buffer;
-    partition_info tmp_ext;
-
-    memset(&tmp_ext, 0, sizeof tmp_ext);
-    tmp_ext.id = FREE_SPACE;
-    tmp_ext.num = PRIMARY;
-
-    if ((fd = open(disk_device, O_RDWR)) < 0) {
-        if ((fd = open(disk_device, O_RDONLY)) < 0)
-             fatal(_("Cannot open disk drive"), 2);
-        opentype = O_RDONLY;
-        print_warning(_("Opened disk read-only - you have no permission to write"));
-        if (curses_started) {
-             refresh();
-             getch();
-             clear_warning();
-        }
-    } else
-        opentype = O_RDWR;
-    opened = TRUE;
-
-#ifdef BLKFLSBUF
-    /* Blocks are visible in more than one way:
-       e.g. as block on /dev/hda and as block on /dev/hda3
-       By a bug in the Linux buffer cache, we will see the old
-       contents of /dev/hda when the change was made to /dev/hda3.
-       In order to avoid this, discard all blocks on /dev/hda.
-       Note that partition table blocks do not live in /dev/hdaN,
-       so this only plays a role if we want to show volume labels. */
-    ioctl(fd, BLKFLSBUF);      /* ignore errors */
-                               /* e.g. Permission Denied */
-#endif
-
-    if (blkdev_get_sectors(fd, &llsectors) == -1)
-           fatal(_("Cannot get disk size"), 3);
-    actual_size = llsectors;
-
-    read_sector(buffer.c.b, 0);
-
-    get_kernel_geometry();
-
-    if (!zero_table || use_partition_table_geometry)
-       get_partition_table_geometry(& buffer);
-
-    decide_on_geometry();
-
-    clear_p_info();
-
-    if (!zero_table) {
-       char *errmsg = "";
+static void ui_draw_menu(struct cfdisk *cf)
+{
+       struct cfdisk_menudesc *d;
+       size_t i = 0;
 
-       for (i = 0; i < 4; i++) {
-           p = & buffer.p.part[i];
-           bs = get_start_sect(p);
-           bsz = get_nr_sects(p);
+       assert(cf);
+       assert(cf->menu);
 
-           if (p->sys_ind > 0 &&
-               add_part(i, p->sys_ind, p->boot_ind,
-                        ((bs <= sectors) ? 0 : bs), bs + bsz - 1,
-                        ((bs <= sectors) ? bs : 0), 1, &errmsg)) {
-                   char *bad = _("Bad primary partition");
-                   char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
-                   sprintf(msg, "%s %d: %s", bad, i + 1, errmsg);
-                   fatal(msg, 4);
-           }
-           if (is_extended(buffer.p.part[i].sys_ind))
-               tmp_ext = ext_info;
-       }
+       DBG(FRONTEND, dbgprint("ui: menu: draw start"));
 
-       if (is_extended(tmp_ext.id)) {
-           ext_info = tmp_ext;
-           logical_sectors[logical] =
-                ext_info.first_sector + ext_info.offset;
-           read_sector(buffer.c.b, logical_sectors[logical++]);
-           i = 4;
-           do {
-               for (pn = 0;
-                    pn < 4 && (!buffer.p.part[pn].sys_ind ||
-                              is_extended(buffer.p.part[pn].sys_ind));
-                    pn++);
-
-               if (pn < 4) {
-                       p = & buffer.p.part[pn];
-                       bs = get_start_sect(p);
-                       bsz = get_nr_sects(p);
-
-                       if (add_part(i++, p->sys_ind, p->boot_ind,
-                            logical_sectors[logical-1],
-                            logical_sectors[logical-1] + bs + bsz - 1,
-                            bs, 1, &errmsg)) {
-                               char *bad = _("Bad logical partition");
-                               char *msg = (char *) xmalloc(strlen(bad) + strlen(errmsg) + 30);
-                               sprintf(msg, "%s %d: %s", bad, i, errmsg);
-                               fatal(msg, 4);
-                       }
-               }
+       menu_update_ignore(cf);
 
-               for (pn = 0;
-                    pn < 4 && !is_extended(buffer.p.part[pn].sys_ind);
-                    pn++);
-               if (pn < 4) {
-                       p = & buffer.p.part[pn];
-                       bs = get_start_sect(p);
-                       logical_sectors[logical] = ext_info.first_sector
-                               + ext_info.offset + bs;
-                       read_sector(buffer.c.b, logical_sectors[logical++]);
-               }
-           } while (pn < 4 && logical < MAXIMUM_PARTS-4);
-       }
-    }
-}
+       while ((d = menu_get_menuitem(cf, i)))
+               ui_draw_menuitem(cf, d, i++);
 
-static void
-fill_part_table(struct partition *p, partition_info *pi) {
-    long long begin;
-
-    p->boot_ind = pi->flags;
-    p->sys_ind = pi->id;
-    begin = pi->first_sector + pi->offset;
-    if (IS_LOGICAL(pi->num))
-       set_start_sect(p,pi->offset);
-    else
-       set_start_sect(p,begin);
-    set_nr_sects(p, pi->last_sector - begin + 1);
-    set_hsc_begin(p, begin);
-    set_hsc_end(p, pi->last_sector);
+       DBG(FRONTEND, dbgprint("ui: menu: draw end."));
 }
 
-static void
-fill_primary_table(partition_table *buffer) {
-    int i;
-
-    /* Zero out existing table */
-    for (i = 0x1BE; i < SECTOR_SIZE; i++)
-       buffer->c.b[i] = 0;
-
-    for (i = 0; i < num_parts; i++)
-       if (IS_PRIMARY(p_info[i].num))
-           fill_part_table(&(buffer->p.part[p_info[i].num]), &(p_info[i]));
+static void ui_menu_goto(struct cfdisk *cf, int where)
+{
+       struct cfdisk_menudesc *d;
+       size_t old;
+
+       if (where < 0)
+               where = cf->menu->nitems - 1;
+       else if ((size_t) where > cf->menu->nitems - 1)
+               where = 0;
+       if ((size_t) where == cf->menu_idx)
+               return;
 
-    if (is_extended(ext_info.id))
-       fill_part_table(&(buffer->p.part[ext_info.num]), &ext_info);
+       old = cf->menu_idx;
+       cf->menu_idx = where;
 
-    buffer->p.magicflag[0] = PART_TABLE_FLAG0;
-    buffer->p.magicflag[1] = PART_TABLE_FLAG1;
-}
+       d = menu_get_menuitem(cf, old);
+       ui_draw_menuitem(cf, d, old);
 
-static void
-fill_logical_table(partition_table *buffer, partition_info *pi) {
-    struct partition *p;
-    int i;
-
-    for (i = 0; i < logical && pi->first_sector != logical_sectors[i]; i++);
-    if (i == logical || buffer->p.magicflag[0] != PART_TABLE_FLAG0
-                    || buffer->p.magicflag[1] != PART_TABLE_FLAG1)
-       for (i = 0; i < SECTOR_SIZE; i++)
-           buffer->c.b[i] = 0;
-
-    /* Zero out existing table */
-    for (i = 0x1BE; i < SECTOR_SIZE; i++)
-       buffer->c.b[i] = 0;
-
-    fill_part_table(&(buffer->p.part[0]), pi);
-
-    for (i = 0;
-        i < num_parts && pi->num != p_info[i].num - 1;
-        i++);
-
-    if (i < num_parts) {
-       p = &(buffer->p.part[1]);
-       pi = &(p_info[i]);
-
-       p->boot_ind = 0;
-       p->sys_ind = DOS_EXTENDED;
-       set_start_sect(p, pi->first_sector - ext_info.first_sector - ext_info.offset);
-       set_nr_sects(p, pi->last_sector - pi->first_sector + 1);
-       set_hsc_begin(p, pi->first_sector);
-       set_hsc_end(p, pi->last_sector);
-    }
-
-    buffer->p.magicflag[0] = PART_TABLE_FLAG0;
-    buffer->p.magicflag[1] = PART_TABLE_FLAG1;
+       d = menu_get_menuitem(cf, where);
+       ui_draw_menuitem(cf, d, where);
 }
 
-static void
-write_part_table(void) {
-    int i, ct, done = FALSE, len;
-    partition_table buffer;
-    struct stat s;
-    int is_bdev;
-    char response[LINE_LENGTH];
-
-    if (opentype == O_RDONLY) {
-        print_warning(_("Opened disk read-only - you have no permission to write"));
-        refresh();
-        getch();
-        clear_warning();
-        return;
-    }
-
-    is_bdev = 0;
-    if(fstat(fd, &s) == 0 && S_ISBLK(s.st_mode))
-        is_bdev = 1;
-
-    if (is_bdev) {
-        print_warning(_("Warning!!  This may destroy data on your disk!"));
-
-        while (!done) {
-             mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                      _("Are you sure you want to write the partition table "
-                      "to disk? (yes or no): "));
-             len = get_string(response, LINE_LENGTH, NULL);
-             clear_warning();
-             if (len == GS_ESCAPE)
-                  return;
-             else if (strcasecmp(response, _("no")) == 0 ||
-                      strcasecmp(response, "no") == 0) {
-                  print_warning(_("Did not write partition table to disk"));
-                  return;
-             } else if (strcasecmp(response, _("yes")) == 0 ||
-                        strcasecmp(response, "yes") == 0)
-                  done = TRUE;
-             else
-                  print_warning(_("Please enter `yes' or `no'"));
-        }
-
-        clear_warning();
-        print_warning(_("Writing partition table to disk..."));
-        refresh();
-    }
-
-    read_sector(buffer.c.b, 0);
-    fill_primary_table(&buffer);
-    write_sector(buffer.c.b, 0);
-
-    for (i = 0; i < num_parts; i++)
-       if (IS_LOGICAL(p_info[i].num)) {
-           read_sector(buffer.c.b, p_info[i].first_sector);
-           fill_logical_table(&buffer, &(p_info[i]));
-           write_sector(buffer.c.b, p_info[i].first_sector);
-       }
-
-    if (is_bdev) {
-#ifdef BLKRRPART
-        sync();
-        if (!ioctl(fd,BLKRRPART))
-             changed = TRUE;
-#endif
-        sync();
-
-        clear_warning();
-        if (changed)
-             print_warning(_("Wrote partition table to disk"));
-        else
-             print_warning(_("Wrote partition table, but re-read table failed.  Run partprobe(8), kpartx(8) or reboot to update table."));
-    } else
-        print_warning(_("Wrote partition table to disk"));
-
-    /* Check: unique bootable primary partition? */
-    ct = 0;
-    for (i = 0; i < num_parts; i++)
-       if (IS_PRIMARY(i) && p_info[i].flags == ACTIVE_FLAG)
-           ct++;
-    if (ct == 0)
-       print_warning(_("No primary partitions are marked bootable. DOS MBR cannot boot this."));
-    if (ct > 1)
-       print_warning(_("More than one primary partition is marked bootable. DOS MBR cannot boot this."));
+static int ui_menu_action(struct cfdisk *cf, int key)
+{
+       return 0;
 }
 
-static void
-fp_printf(FILE *fp, char *format, ...) {
-    va_list args;
-    char buf[1024];
-    int y, x __attribute__((unused));
-
-    va_start(args, format);
-    vsnprintf(buf, sizeof(buf), format, args);
-    va_end(args);
-
-    if (fp == NULL) {
-       /* The following works best if the string to be printed has at
-           most only one newline. */
-       printw("%s", buf);
-       getyx(stdscr, y, x);
-       if (y >= COMMAND_LINE_Y-2) {
-           menuContinue();
-           erase();
-           move(0, 0);
-       }
-    } else
-       fprintf(fp, "%s", buf);
-}
+static void ui_draw_partition(struct cfdisk *cf, size_t i)
+{
+       int ln = TABLE_START_LINE + 1 + i;      /* skip table header */
+       int cl = ARROW_CURSOR_WIDTH;            /* we need extra space for cursor */
 
-#define MAX_PER_LINE 16
-static void
-print_file_buffer(FILE *fp, unsigned char *buffer) {
-    int i,l;
-
-    for (i = 0, l = 0; i < SECTOR_SIZE; i++, l++) {
-       if (l == 0)
-           fp_printf(fp, "0x%03X:", i);
-       fp_printf(fp, " %02X", buffer[i]);
-       if (l == MAX_PER_LINE - 1) {
-           fp_printf(fp, "\n");
-           l = -1;
-       }
-    }
-    if (l > 0)
-       fp_printf(fp, "\n");
-    fp_printf(fp, "\n");
-}
+       DBG(FRONTEND, dbgprint("ui: draw partition %zu", i));
 
-static void
-print_raw_table(void) {
-    int i, to_file;
-    partition_table buffer;
-    char fname[LINE_LENGTH];
-    FILE *fp;
-
-    if (print_only) {
-       fp = stdout;
-       to_file = TRUE;
-    } else {
-       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                _("Enter filename or press RETURN to display on screen: "));
-
-       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
-           return;
-
-       if (to_file) {
-           if ((fp = fopen(fname, "w")) == NULL) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, sizeof(errstr),
-                        _("cannot open %s"), fname);
-               print_warning(errstr);
-               return;
-           }
+       if (cf->lines_idx == i) {
+               standout();
+               mvaddstr(ln, 0, ARROW_CURSOR_STRING);
+               mvaddstr(ln, cl, cf->lines[i + 1]);
+               standend();
        } else {
-           fp = NULL;
-           erase();
-           move(0, 0);
-       }
-    }
-
-    fp_printf(fp, _("Disk Drive: %s\n"), disk_device);
-
-    fp_printf(fp, _("Sector 0:\n"));
-    read_sector(buffer.c.b, 0);
-    fill_primary_table(&buffer);
-    print_file_buffer(fp, buffer.c.b);
-
-    for (i = 0; i < num_parts; i++)
-       if (IS_LOGICAL(p_info[i].num)) {
-           fp_printf(fp, _("Sector %d:\n"), p_info[i].first_sector);
-           read_sector(buffer.c.b, p_info[i].first_sector);
-           fill_logical_table(&buffer, &(p_info[i]));
-           print_file_buffer(fp, buffer.c.b);
+               mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
+               mvaddstr(ln, cl, cf->lines[i + 1]);
        }
 
-    if (to_file) {
-       if (!print_only)
-           if (close_stream(fp) != 0) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, sizeof(errstr), _("write failed: %s"), fname);
-               print_warning(errstr);
-           }
-    } else {
-        menuContinue();
-    }
-}
-
-static void
-print_p_info_entry(FILE *fp, partition_info *p) {
-    long long size;
-    char part_str[40];
-
-    if (p->id == UNUSABLE)
-       fp_printf(fp, _("   None   "));
-    else if (p->id == FREE_SPACE && p->num == PRI_OR_LOG)
-       fp_printf(fp, _("   Pri/Log"));
-    else if (p->id == FREE_SPACE && p->num == PRIMARY)
-       fp_printf(fp, _("   Primary"));
-    else if (p->id == FREE_SPACE && p->num == LOGICAL)
-       fp_printf(fp, _("   Logical"));
-    else
-       fp_printf(fp, "%2d %-7.7s", p->num+1,
-                 IS_LOGICAL(p->num) ? _("Logical") : _("Primary"));
-
-    fp_printf(fp, " ");
-
-    fp_printf(fp, "%11lld%c", p->first_sector,
-             ((p->first_sector/cylinder_size) !=
-              ((float)p->first_sector/cylinder_size) ?
-              '*' : ' '));
-
-    fp_printf(fp, "%11lld%c", p->last_sector,
-             (((p->last_sector+1)/cylinder_size) !=
-              ((float)(p->last_sector+1)/cylinder_size) ?
-              '*' : ' '));
-
-    fp_printf(fp, "%6ld%c", p->offset,
-             ((((p->first_sector == 0 || IS_LOGICAL(p->num)) &&
-                (p->offset != sectors)) ||
-               (p->first_sector != 0 && IS_PRIMARY(p->num) &&
-                p->offset != 0)) ?
-              '#' : ' '));
-
-    size = p->last_sector - p->first_sector + 1;
-    fp_printf(fp, "%11lld%c", size,
-             ((size/cylinder_size) != ((float)size/cylinder_size) ?
-              '*' : ' '));
-
-    /* fp_printf(fp, " "); */
-
-    if (p->id == UNUSABLE)
-       sprintf(part_str, "%.15s", _("Unusable"));
-    else if (p->id == FREE_SPACE)
-       sprintf(part_str, "%.15s", _("Free Space"));
-    else if (partition_type_name(p->id))
-       sprintf(part_str, "%.15s (%02X)", _(partition_type_name(p->id)), p->id);
-    else
-       sprintf(part_str, "%.15s (%02X)", _("Unknown"), p->id);
-    fp_printf(fp, "%-20.20s", part_str);
-
-    fp_printf(fp, " ");
-
-    if (p->flags == ACTIVE_FLAG)
-       fp_printf(fp, _("Boot"), p->flags);
-    else if (p->flags != 0)
-       fp_printf(fp, _("(%02X)"), p->flags);
-    else
-       fp_printf(fp, _("None"), p->flags);
-
-    fp_printf(fp, "\n");
-}
-
-static void
-print_p_info(void) {
-    char fname[LINE_LENGTH];
-    FILE *fp;
-    int i, to_file, pext = is_extended(ext_info.id);
-
-    if (print_only) {
-       fp = stdout;
-       to_file = TRUE;
-    } else {
-       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                _("Enter filename or press RETURN to display on screen: "));
-
-       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
-           return;
-
-       if (to_file) {
-           if ((fp = fopen(fname, "w")) == NULL) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, LINE_LENGTH, _("cannot open %s"), fname);
-               print_warning(errstr);
-               return;
-           }
-       } else {
-           fp = NULL;
-           erase();
-           move(0, 0);
-       }
-    }
-
-    fp_printf(fp, _("Partition Table for %s\n"), disk_device);
-    fp_printf(fp, "\n");
-    fp_printf(fp, _("               First       Last\n"));
-    fp_printf(fp, _(" # Type       Sector      Sector   Offset    Length   Filesystem Type (ID) Flag\n"));
-    fp_printf(fp, _("-- ------- ----------- ----------- ------ ----------- -------------------- ----\n"));
-
-    for (i = 0; i < num_parts; i++) {
-       if (pext && (p_info[i].first_sector >= ext_info.first_sector)) {
-           print_p_info_entry(fp,&ext_info);
-           pext = FALSE;
-       }
-       print_p_info_entry(fp, &(p_info[i]));
-    }
-
-    if (to_file) {
-       if (!print_only)
-           if (close_stream(fp) != 0) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, sizeof(errstr), _("write failed: %s"), fname);
-               print_warning(errstr);
-           }
-    } else {
-        menuContinue();
-    }
 }
 
-static void
-print_part_entry(FILE *fp, int num, partition_info *pi) {
-    long long first = 0, start = 0, end = 0, size = 0;
-    unsigned char ss, es, sh, eh;
-    int sc, ec;
-    int flags = 0, id = 0;
-
-    ss = sh = es = eh = 0;
-    sc = ec = 0;
+static int ui_draw_table(struct cfdisk *cf)
+{
+       int cl = ARROW_CURSOR_WIDTH;
+       size_t i, nparts = fdisk_table_get_nents(cf->table);
 
-    if (pi != NULL) {
-       flags = pi->flags;
-       id = pi->id;
+       DBG(FRONTEND, dbgprint("ui: draw table"));
 
-       if (IS_LOGICAL(num))
-           first = pi->offset;
-       else
-           first = pi->first_sector + pi->offset;
+       if (cf->nlines - 2 < cf->lines_idx)
+               cf->lines_idx = cf->nlines - 2; /* don't count header */
 
-       start = pi->first_sector + pi->offset;
-       end = pi->last_sector;
-       size = end - start + 1;
+       /* print header */
+       attron(A_BOLD);
+       mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
+       attroff(A_BOLD);
 
-       set_hsc0(&sh, &ss, &sc, start);
-       set_hsc0(&eh, &es, &ec, end);
-    }
+       /* print partitions */
+       for (i = 0; i < nparts; i++)
+               ui_draw_partition(cf, i);
 
-    fp_printf(fp, "%2d  0x%02X %4d %4d %5d 0x%02X %4d %4d %5d %11lld %11lld\n",
-             num+1, flags, sh, ss, sc, id, eh, es, ec, first, size);
+       return 0;
 }
 
+static int ui_table_goto(struct cfdisk *cf, int where)
+{
+       size_t old;
+       size_t nparts = fdisk_table_get_nents(cf->table);
 
-static void
-print_part_table(void) {
-    int i, j, to_file;
-    char fname[LINE_LENGTH];
-    FILE *fp;
+       DBG(FRONTEND, dbgprint("ui: goto table %d", where));
 
-    if (print_only) {
-       fp = stdout;
-       to_file = TRUE;
-    } else {
-       mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                _("Enter filename or press RETURN to display on screen: "));
+       if (where < 0)
+               where = 0;
+       else if ((size_t) where > nparts - 1)
+               where = nparts - 1;
 
-       if ((to_file = get_string(fname, LINE_LENGTH, NULL)) < 0)
-           return;
+       if ((size_t) where == cf->lines_idx)
+               return 0;
 
-       if (to_file) {
-           if ((fp = fopen(fname, "w")) == NULL) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, LINE_LENGTH, _("cannot open %s"), fname);
-               print_warning(errstr);
-               return;
-           }
-       } else {
-           fp = NULL;
-           erase();
-           move(0, 0);
-       }
-    }
-
-    fp_printf(fp, _("Partition Table for %s\n"), disk_device);
-    fp_printf(fp, "\n");
-    /* Three-line heading. Read "Start Sector" etc vertically. */
-    fp_printf(fp, _("         ---Starting----      ----Ending-----    Start     Number of\n"));
-    fp_printf(fp, _(" # Flags Head Sect  Cyl   ID  Head Sect  Cyl     Sector    Sectors\n"));
-    fp_printf(fp, _("-- ----- ---- ---- ----- ---- ---- ---- ----- ----------- -----------\n"));
-
-    for (i = 0; i < 4; i++) {
-       for (j = 0;
-            j < num_parts && (p_info[j].id <= 0 || p_info[j].num != i);
-            j++);
-       if (j < num_parts) {
-           print_part_entry(fp, i, &(p_info[j]));
-       } else if (is_extended(ext_info.id) && ext_info.num == i) {
-           print_part_entry(fp, i, &ext_info);
-       } else {
-           print_part_entry(fp, i, NULL);
-       }
-    }
-
-    for (i = 0; i < num_parts; i++)
-       if (IS_LOGICAL(p_info[i].num))
-           print_part_entry(fp, p_info[i].num, &(p_info[i]));
-
-    if (to_file) {
-       if (!print_only)
-           if (close_stream(fp) != 0) {
-               char errstr[LINE_LENGTH];
-               snprintf(errstr, sizeof(errstr), _("write failed: %s"), fname);
-               print_warning(errstr);
-           }
-    } else {
-        menuContinue();
-    }
-}
+       old = cf->lines_idx;
+       cf->lines_idx = where;
 
-static void
-print_tables(void) {
-    int done = FALSE;
-
-    static struct MenuItem menuFormat[]=
-    {
-        { 'r', N_("Raw"), N_("Print the table using raw data format") },
-        { 's', N_("Sectors"), N_("Print the table ordered by sectors") },
-        { 't', N_("Table"), N_("Just print the partition table") },
-        { ESC, N_("Cancel"), N_("Don't print the table") },
-        { 0, NULL, NULL }
-    };
-
-    while (!done)
-       switch ( toupper(menuSimple( menuFormat, 2)) ) {
-       case 'R':
-           print_raw_table();
-           done = TRUE;
-           break;
-       case 'S':
-           print_p_info();
-           done = TRUE;
-           break;
-       case 'T':
-           print_part_table();
-           done = TRUE;
-           break;
-       case ESC:
-           done = TRUE;
-           break;
-       }
+       ui_draw_partition(cf, old);     /* cleanup old */
+       ui_draw_partition(cf, where);   /* draw new */
+       ui_draw_menu(cf);
+       refresh();
+       return 0;
 }
 
-#define END_OF_HELP "EOHS!"
-static void
-display_help(void) {
-    char *help_text[] = {
-       N_("Help Screen for cfdisk"),
-       "",
-       N_("This is cfdisk, a curses based disk partitioning program, which"),
-       N_("allows you to create, delete and modify partitions on your hard"),
-       N_("disk drive."),
-       "",
-       N_("Copyright (C) 1994-1999 Kevin E. Martin & aeb"),
-       "",
-       N_("Command      Meaning"),
-       N_("-------      -------"),
-       N_("  b          Toggle bootable flag of the current partition"),
-       N_("  d          Delete the current partition"),
-       N_("  g          Change cylinders, heads, sectors-per-track parameters"),
-       N_("             WARNING: This option should only be used by people who"),
-       N_("             know what they are doing."),
-       N_("  h          Print this screen"),
-       N_("  m          Maximize disk usage of the current partition"),
-       N_("             Note: This may make the partition incompatible with"),
-       N_("             DOS, OS/2, ..."),
-       N_("  n          Create new partition from free space"),
-       N_("  p          Print partition table to the screen or to a file"),
-       N_("             There are several different formats for the partition"),
-       N_("             that you can choose from:"),
-       N_("                r - Raw data (exactly what would be written to disk)"),
-       N_("                s - Table ordered by sectors"),
-       N_("                t - Table in raw format"),
-       N_("  q          Quit program without writing partition table"),
-       N_("  t          Change the filesystem type"),
-       N_("  u          Change units of the partition size display"),
-       N_("             Rotates through MB, sectors and cylinders"),
-       N_("  W          Write partition table to disk (must enter upper case W)"),
-        N_("             Since this might destroy data on the disk, you must"),
-       N_("             either confirm or deny the write by entering `yes' or"),
-       N_("             `no'"),
-       N_("Up Arrow     Move cursor to the previous partition"),
-       N_("Down Arrow   Move cursor to the next partition"),
-       N_("CTRL-L       Redraws the screen"),
-       N_("  ?          Print this screen"),
-       "",
-       N_("Note: All of the commands can be entered with either upper or lower"),
-       N_("case letters (except for Writes)."),
-       END_OF_HELP
-    };
-
-    int cur_line = 0;
-    FILE *fp = NULL;
-
-    erase();
-    move(0, 0);
-    while (strcmp(help_text[cur_line], END_OF_HELP)) {
-       if (help_text[cur_line][0])
-           fp_printf(fp, "%s\n", _(help_text[cur_line]));
+static int ui_refresh(struct cfdisk *cf)
+{
+       char *id = NULL;
+        uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size;
+       char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
+                               | SIZE_SUFFIX_3LETTER, bytes);
+       erase();
+
+       if (!cf->ui_enabled)
+               return -EINVAL;
+
+       /* header */
+       attron(A_BOLD);
+       ui_print_center(0, _("Disk: %s"), cf->cxt->dev_path);
+       attroff(A_BOLD);
+       ui_print_center(1, _("Size: %s, %ju bytes, %ju sectors"),
+                       strsz, bytes, (uintmax_t) cf->cxt->total_sectors);
+       if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
+               ui_print_center(2, _("Label: %s, identifier: %s"),
+                               cf->cxt->label->name, id);
        else
-           fp_printf(fp, "\n");
-       cur_line++;
-    }
-    menuContinue();
-}
+               ui_print_center(2, _("Label: %s"));
+       free(strsz);
 
-static int
-change_geometry(void) {
-    int ret_val = FALSE;
-    int done = FALSE;
-    char def[LINE_LENGTH];
-    char response[LINE_LENGTH];
-    long long tmp_val;
-    int i;
-
-    while (!done) {
-        static struct MenuItem menuGeometry[]=
-        {
-            { 'c', N_("Cylinders"), N_("Change cylinder geometry") },
-            { 'h', N_("Heads"), N_("Change head geometry") },
-            { 's', N_("Sectors"), N_("Change sector geometry") },
-            { 'd', N_("Done"), N_("Done with changing geometry") },
-            { 0, NULL, NULL }
-        };
-       move(COMMAND_LINE_Y, COMMAND_LINE_X);
-       clrtoeol();
+       ui_draw_table(cf);
+       ui_draw_menu(cf);
        refresh();
-
-       clear_warning();
-
-       switch (toupper( menuSimple(menuGeometry, 3) )) {
-       case 'C':
-           sprintf(def, "%llu", actual_size/cylinder_size);
-           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                    _("Enter the number of cylinders: "));
-           i = get_string(response, LINE_LENGTH, def);
-           if (i == GS_DEFAULT) {
-               user_cylinders = actual_size/cylinder_size;
-               ret_val = TRUE;
-           } else if (i > 0) {
-               tmp_val = atoll(response);
-               if (tmp_val > 0) {
-                   user_cylinders = tmp_val;
-                   ret_val = TRUE;
-               } else
-                   print_warning(_("Illegal cylinders value"));
-           }
-           break;
-       case 'H':
-           sprintf(def, "%d", heads);
-           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                    _("Enter the number of heads: "));
-           if (get_string(response, LINE_LENGTH, def) > 0) {
-               tmp_val = atoll(response);
-               if (tmp_val > 0 && tmp_val <= MAX_HEADS) {
-                   user_heads = tmp_val;
-                   ret_val = TRUE;
-               } else
-                   print_warning(_("Illegal heads value"));
-           }
-           break;
-       case 'S':
-           sprintf(def, "%d", sectors);
-           mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X,
-                    _("Enter the number of sectors per track: "));
-           if (get_string(response, LINE_LENGTH, def) > 0) {
-               tmp_val = atoll(response);
-               if (tmp_val > 0 && tmp_val <= MAX_SECTORS) {
-                   user_sectors = tmp_val;
-                   ret_val = TRUE;
-               } else
-                   print_warning(_("Illegal sectors value"));
-           }
-           break;
-       case ESC:
-       case 'D':
-           done = TRUE;
-           break;
-       default:
-           putchar(BELL);
-           break;
-       }
-
-       if (ret_val) {
-           decide_on_geometry();
-           draw_screen();
-       }
-    }
-
-    if (ret_val) {
-       long long disk_end;
-
-       disk_end = actual_size-1;
-
-       if (p_info[num_parts-1].last_sector > disk_end) {
-           while (p_info[num_parts-1].first_sector > disk_end) {
-               if (p_info[num_parts-1].id == FREE_SPACE ||
-                   p_info[num_parts-1].id == UNUSABLE)
-                   remove_part(num_parts-1);
-               else
-                   del_part(num_parts-1);
-           }
-
-           p_info[num_parts-1].last_sector = disk_end;
-
-           if (ext_info.last_sector > disk_end)
-               ext_info.last_sector = disk_end;
-       } else if (p_info[num_parts-1].last_sector < disk_end) {
-           if (p_info[num_parts-1].id == FREE_SPACE ||
-               p_info[num_parts-1].id == UNUSABLE) {
-               p_info[num_parts-1].last_sector = disk_end;
-           } else {
-               insert_empty_part(num_parts,
-                                 p_info[num_parts-1].last_sector+1,
-                                 disk_end);
-           }
-       }
-
-       /* Make sure the partitions are correct */
-       check_part_info();
-    }
-
-    return ret_val;
-}
-
-static void
-change_id(int i) {
-    char id[LINE_LENGTH], def[LINE_LENGTH];
-    int num_types = 0;
-    int num_across, num_down;
-    int len, new_id = ((p_info[i].id == LINUX) ? LINUX_SWAP : LINUX);
-    int y_start, y_end, row, row_min, row_max, row_offset, j, needmore;
-
-    for (j = 1; i386_sys_types[j].name; j++) ;
-    num_types = j-1;           /* do not count the Empty type */
-
-    num_across = COLS/COL_ID_WIDTH;
-    num_down = (((float)num_types)/num_across + 1);
-    y_start = COMMAND_LINE_Y - 1 - num_down;
-    if (y_start < 1) {
-       y_start = 1;
-       y_end = COMMAND_LINE_Y - 2;
-    } else {
-       if (y_start > DISK_TABLE_START+cur_part+4)
-           y_start = DISK_TABLE_START+cur_part+4;
-       y_end = y_start + num_down - 1;
-    }
-
-    row_min = 1;
-    row_max = COMMAND_LINE_Y - 2;
-    row_offset = 0;
-    do {
-       for (j = y_start - 1; j <= y_end + 1; j++) {
-           move(j, 0);
-           clrtoeol();
-       }
-       needmore = 0;
-       for (j = 1; i386_sys_types[j].name; j++) {
-           row = y_start + (j-1) % num_down - row_offset;
-           if (row >= row_min && row <= row_max) {
-               move(row, ((j-1)/num_down)*COL_ID_WIDTH + 1);
-               printw("%02X %-20.20s",
-                      i386_sys_types[j].type,
-                      _(i386_sys_types[j].name));
-           }
-           if (row > row_max)
-               needmore = 1;
-       }
-       if (needmore)
-               menuContinue();
-       row_offset += (row_max - row_min + 1);
-    } while(needmore);
-
-    sprintf(def, "%02X", new_id);
-    mvaddstr(COMMAND_LINE_Y, COMMAND_LINE_X, _("Enter filesystem type: "));
-    if ((len = get_string(id, 3, def)) <= 0 && len != GS_DEFAULT)
-       return;
-
-    if (len != GS_DEFAULT) {
-       if (!isxdigit(id[0]))
-           return;
-       new_id = (isdigit(id[0]) ? id[0] - '0' : tolower(id[0]) - 'a' + 10);
-       if (len == 2) {
-           if (isxdigit(id[1]))
-               new_id = new_id*16 +
-                   (isdigit(id[1]) ? id[1] - '0' : tolower(id[1]) - 'a' + 10);
-           else
-               return;
-       }
-    }
-
-    if (new_id == 0)
-       print_warning(_("Cannot change FS Type to empty"));
-    else if (is_extended(new_id))
-       print_warning(_("Cannot change FS Type to extended"));
-    else
-       p_info[i].id = new_id;
+       return 0;
 }
 
-static void
-draw_partition(int i) {
-    int j;
-    int y = i + DISK_TABLE_START + 2 - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
-    char *t;
-    long long size;
-    double fsize;
-
-    if (!arrow_cursor) {
-       move(y, 0);
-       for (j = 0; j < COLS; j++)
-           addch(' ');
-    }
-
-    if (p_info[i].id > 0) {
-       char *dbn = my_basename(disk_device);
-       int l = strlen(dbn);
-       int digit_last = isdigit(dbn[l-1]);
-
-       mvprintw(y, NAME_START,
-                "%s%s%d", dbn, (digit_last ? "p" : ""),
-                p_info[i].num+1);
-       if (p_info[i].flags) {
-           if (p_info[i].flags == ACTIVE_FLAG)
-               mvaddstr(y, FLAGS_START, _("Boot"));
-           else
-               mvprintw(y, FLAGS_START, _("Unk(%02X)"), p_info[i].flags);
-           if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
-               if (p_info[i].offset != sectors)
-                   addstr(_(", NC"));
-           } else {
-               if (p_info[i].offset != 0)
-                   addstr(_(", NC"));
-           }
-       } else {
-           if (p_info[i].first_sector == 0 || IS_LOGICAL(p_info[i].num)) {
-               if (p_info[i].offset != sectors)
-                   mvaddstr(y, FLAGS_START, _("NC"));
-           } else {
-               if (p_info[i].offset != 0)
-                   mvaddstr(y, FLAGS_START, _("NC"));
-           }
-       }
-    }
-    mvaddstr(y, PTYPE_START,
-            (p_info[i].id == UNUSABLE ? "" :
-             (IS_LOGICAL(p_info[i].num) ? _("Logical") :
-              (p_info[i].num >= 0 ? _("Primary") :
-               (p_info[i].num == PRI_OR_LOG ? _("Pri/Log") :
-                (p_info[i].num == PRIMARY ? _("Primary") : _("Logical")))))));
-
-    t = partition_type_text(i);
-    if (t)
-        mvaddstr(y, FSTYPE_START, t);
-    else
-        mvprintw(y, FSTYPE_START, _("Unknown (%02X)"), p_info[i].id);
-
-    if (p_info[i].volume_label[0]) {
-       int l = strlen(p_info[i].volume_label);
-       int s = SIZE_START-5-l;
-       mvprintw(y, (s > LABEL_START) ? LABEL_START : s,
-                " [%s]  ", p_info[i].volume_label);
-    }
-
-    size = p_info[i].last_sector - p_info[i].first_sector + 1;
-    fsize = (double) size * SECTOR_SIZE;
-    if (display_units == SECTORS)
-       mvprintw(y, SIZE_START, "%11lld", size);
-    else if (display_units == CYLINDERS)
-       mvprintw(y, SIZE_START, "%11lld", size/cylinder_size);
-    else if (display_units == MEGABYTES)
-       mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K))/100);
-    else if (display_units == GIGABYTES)
-       mvprintw(y, SIZE_START, "%11.2f", ceiling((100*fsize)/(K*K*K))/100);
-    if (size % cylinder_size != 0 ||
-       p_info[i].first_sector % cylinder_size != 0)
-       mvprintw(y, COLUMNS-1, "*");
-}
+static int ui_run(struct cfdisk *cf)
+{
+       int rc;
 
-static void
-init_const(void) {
-    if (!defined) {
-       NAME_START = (((float)NAME_START)/COLUMNS)*COLS;
-       FLAGS_START = (((float)FLAGS_START)/COLUMNS)*COLS;
-       PTYPE_START = (((float)PTYPE_START)/COLUMNS)*COLS;
-       FSTYPE_START = (((float)FSTYPE_START)/COLUMNS)*COLS;
-       LABEL_START = (((float)LABEL_START)/COLUMNS)*COLS;
-       SIZE_START = (((float)SIZE_START)/COLUMNS)*COLS;
-       COMMAND_LINE_X = (((float)COMMAND_LINE_X)/COLUMNS)*COLS;
-
-       COMMAND_LINE_Y = LINES - 4;
-       WARNING_START = LINES - 2;
-
-       if ((NUM_ON_SCREEN = COMMAND_LINE_Y - DISK_TABLE_START - 3) <= 0)
-           NUM_ON_SCREEN = 1;
-
-       COLUMNS = COLS;
-       defined = TRUE;
-    }
-}
+       DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES));
 
-static void
-draw_screen(void) {
-    int i;
-    char *line;
+       menu_push(cf, CFDISK_MENU_MAIN);
 
-    line = (char *) xmalloc((COLS+1)*sizeof(char));
+       rc = ui_refresh(cf);
+       if (rc)
+               return rc;
 
-    if (warning_last_time) {
-       for (i = 0; i < COLS; i++) {
-           move(WARNING_START, i);
-           line[i] = inch();
-       }
-       line[COLS] = 0;
-    }
-
-    erase();
-
-    if (warning_last_time)
-       mvaddstr(WARNING_START, 0, line);
-
-
-    snprintf(line, COLS+1, "cfdisk (%s)", PACKAGE_STRING);
-    mvaddstr(HEADER_START, (COLS-strlen(line))/2, line);
-    snprintf(line, COLS+1, _("Disk Drive: %s"), disk_device);
-    mvaddstr(HEADER_START+2, (COLS-strlen(line))/2, line);
-    {
-           long long bytes = actual_size*(long long) SECTOR_SIZE;
-           long long megabytes = bytes/(K*K);
-
-           if (megabytes < 10000)
-                   sprintf(line, _("Size: %lld bytes, %lld MB"),
-                           bytes, megabytes);
-           else
-                   sprintf(line, _("Size: %lld bytes, %lld.%lld GB"),
-                           bytes, megabytes/K, (10*megabytes/K)%10);
-    }
-    mvaddstr(HEADER_START+3, (COLS-strlen(line))/2, line);
-    snprintf(line, COLS+1, _("Heads: %d   Sectors per Track: %d   Cylinders: %lld"),
-           heads, sectors, cylinders);
-    mvaddstr(HEADER_START+4, (COLS-strlen(line))/2, line);
-
-    mvaddstr(DISK_TABLE_START, NAME_START, _("Name"));
-    mvaddstr(DISK_TABLE_START, FLAGS_START, _("Flags"));
-    mvaddstr(DISK_TABLE_START, PTYPE_START-1, _("Part Type"));
-    mvaddstr(DISK_TABLE_START, FSTYPE_START, _("FS Type"));
-    mvaddstr(DISK_TABLE_START, LABEL_START+1, _("[Label]"));
-    if (display_units == SECTORS)
-       mvaddstr(DISK_TABLE_START, SIZE_START, _("    Sectors"));
-    else if (display_units == CYLINDERS)
-       mvaddstr(DISK_TABLE_START, SIZE_START, _("  Cylinders"));
-    else if (display_units == MEGABYTES)
-       mvaddstr(DISK_TABLE_START, SIZE_START, _("  Size (MB)"));
-    else if (display_units == GIGABYTES)
-       mvaddstr(DISK_TABLE_START, SIZE_START, _("  Size (GB)"));
-
-    move(DISK_TABLE_START+1, 1);
-    for (i = 1; i < COLS-1; i++)
-       addch('-');
-
-    if (NUM_ON_SCREEN >= num_parts)
-       for (i = 0; i < num_parts; i++)
-           draw_partition(i);
-    else
-       for (i = (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN;
-            i < NUM_ON_SCREEN + (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN &&
-            i < num_parts;
-            i++)
-           draw_partition(i);
-
-    free(line);
-}
+       do {
+               int key = getch();
 
-static void
-draw_cursor(int move) {
-    if (move != 0 && (cur_part + move < 0 || cur_part + move >= num_parts)) {
-       print_warning(_("No more partitions"));
-       return;
-    }
-
-    if (arrow_cursor)
-       mvaddstr(DISK_TABLE_START + cur_part + 2
-                - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "   ");
-    else
-       draw_partition(cur_part);
-
-    cur_part += move;
-
-    if (((cur_part - move)/NUM_ON_SCREEN)*NUM_ON_SCREEN !=
-       (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN)
-       draw_screen();
-
-    if (arrow_cursor)
-       mvaddstr(DISK_TABLE_START + cur_part + 2
-                - (cur_part/NUM_ON_SCREEN)*NUM_ON_SCREEN, 0, "-->");
-    else {
-       standout();
-       draw_partition(cur_part);
-       standend();
-    }
-}
+               if (key == 'q')
+                       break;
 
-static void
-do_curses_fdisk(void) {
-    int done = FALSE;
-    int command;
-    int is_first_run = TRUE;
-
-    static struct MenuItem menuMain[] = {
-        { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
-        { 'd', N_("Delete"), N_("Delete the current partition") },
-        { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
-        { 'h', N_("Help"), N_("Print help screen") },
-        { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
-        { 'n', N_("New"), N_("Create new partition from free space") },
-        { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
-        { 'q', N_("Quit"), N_("Quit program without writing partition table") },
-        { 't', N_("Type"), N_("Change the filesystem type (DOS, Linux, OS/2 and so on)") },
-        { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
-        { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
-        { 0, NULL, NULL }
-    };
-    curses_started = 1;
-    initscr();
-    init_const();
-
-    old_SIGINT = signal(SIGINT, die);
-    old_SIGTERM = signal(SIGTERM, die);
-#ifdef DEBUG
-    signal(SIGINT, old_SIGINT);
-    signal(SIGTERM, old_SIGTERM);
+               switch (key) {
+               case KEY_DOWN:
+               case '\016':    /* ^N */
+               case 'j':       /* Vi-like alternative */
+                       ui_table_goto(cf, cf->lines_idx + 1);
+                       break;
+               case KEY_UP:
+               case '\020':    /* ^P */
+               case 'k':       /* Vi-like alternative */
+                       ui_table_goto(cf, cf->lines_idx - 1);
+                       break;
+               case KEY_HOME:
+                       ui_table_goto(cf, 0);
+                       break;
+               case KEY_END:
+                       ui_table_goto(cf, cf->nlines - 1);
+                       break;
+                       ui_menu_action(cf, 0);
+                       break;
+               case KEY_LEFT:
+#ifdef KEY_BTAB
+               case KEY_BTAB:
 #endif
+                       ui_menu_goto(cf, cf->menu_idx - 1);
+                       break;
+               case KEY_RIGHT:
+               case '\t':
+                       ui_menu_goto(cf, cf->menu_idx + 1);
+                       break;
+               case KEY_ENTER:
+               case '\n':
+               case '\r':
+                       ui_menu_action(cf, 0);
+                       break;
+               default:
+                       if (ui_menu_action(cf, key) != 0)
+                               beep();
+                       break;
+               }
+       } while (1);
 
-    cbreak();
-    noecho();
-    nonl();
+       menu_pop(cf);
 
-    fill_p_info();
+       DBG(FRONTEND, dbgprint("ui: end"));
+
+       return 0;
+}
 
-    draw_screen();
+int main(int argc, char *argv[])
+{
+       struct cfdisk _cf = { .lines_idx = 0 },
+                     *cf = &_cf;
 
-    while (!done) {
-       char *s;
+       setlocale(LC_ALL, "");
+       bindtextdomain(PACKAGE, LOCALEDIR);
+       textdomain(PACKAGE);
+       atexit(close_stdout);
 
-       draw_cursor(0);
+       fdisk_init_debug(0);
+       cf->cxt = fdisk_new_context();
+       if (!cf->cxt)
+               err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
 
-       if (p_info[cur_part].id == FREE_SPACE) {
-           s = ((opentype == O_RDWR) ? "hnpquW" : "hnpqu");
-           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
-               s, MENU_BUTTON | MENU_ACCEPT_OTHERS, 5);
-       } else if (p_info[cur_part].id > 0) {
-           s = ((opentype == O_RDWR) ? "bdhmpqtuW" : "bdhmpqtu");
-           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
-               s, MENU_BUTTON | MENU_ACCEPT_OTHERS, is_first_run ? 7 : 0);
-       } else {
-           s = ((opentype == O_RDWR) ? "hpquW" : "hpqu");
-           command = menuSelect(COMMAND_LINE_Y, COMMAND_LINE_X, menuMain, 10,
-               s, MENU_BUTTON | MENU_ACCEPT_OTHERS, 0);
-       }
-       is_first_run = FALSE;
-       switch ( command ) {
-       case 'B':
-       case 'b':
-           if (p_info[cur_part].id > 0)
-               p_info[cur_part].flags ^= 0x80;
-           else
-               print_warning(_("Cannot make this partition bootable"));
-           break;
-       case 'D':
-       case 'd':
-           if (p_info[cur_part].id > 0) {
-               del_part(cur_part);
-               if (cur_part >= num_parts)
-                   cur_part = num_parts - 1;
-               draw_screen();
-           } else
-               print_warning(_("Cannot delete an empty partition"));
-           break;
-       case 'G':
-       case 'g':
-           if (change_geometry())
-               draw_screen();
-           break;
-       case 'M':
-       case 'm':
-           if (p_info[cur_part].id > 0) {
-               if (p_info[cur_part].first_sector == 0 ||
-                   IS_LOGICAL(p_info[cur_part].num)) {
-                   if (p_info[cur_part].offset == sectors)
-                       p_info[cur_part].offset = 1;
-                   else
-                       p_info[cur_part].offset = sectors;
-                   draw_screen();
-               } else if (p_info[cur_part].offset != 0)
-                   p_info[cur_part].offset = 0;
-               else
-                   print_warning(_("Cannot maximize this partition"));
-           } else
-               print_warning(_("Cannot maximize this partition"));
-           break;
-       case 'N':
-       case 'n':
-           if (p_info[cur_part].id == FREE_SPACE) {
-               new_part(cur_part);
-               draw_screen();
-           } else if (p_info[cur_part].id == UNUSABLE)
-               print_warning(_("This partition is unusable"));
-           else
-               print_warning(_("This partition is already in use"));
-           break;
-       case 'P':
-       case 'p':
-           print_tables();
-           draw_screen();
-           break;
-       case 'Q':
-       case 'q':
-           done = TRUE;
-           break;
-       case 'T':
-       case 't':
-           if (p_info[cur_part].id > 0) {
-               change_id(cur_part);
-               draw_screen();
-           } else
-               print_warning(_("Cannot change the type of an empty partition"));
-           break;
-       case 'U':
-       case 'u':
-           if (display_units == GIGABYTES)
-               display_units = MEGABYTES;
-           else if (display_units == MEGABYTES)
-               display_units = SECTORS;
-           else if (display_units == SECTORS)
-               display_units = CYLINDERS;
-           else if (display_units == CYLINDERS)
-               display_units = MEGABYTES;      /* not yet GIGA */
-           draw_screen();
-           break;
-       case 'W':
-           write_part_table();
-           break;
-       case 'H':
-       case 'h':
-       case '?':
-           display_help();
-           draw_screen();
-           break;
-       case KEY_UP:    /* Up arrow key */
-       case '\020':    /* ^P */
-       case 'k':       /* Vi-like alternative */
-           draw_cursor(-1);
-           break;
-       case KEY_DOWN:  /* Down arrow key */
-       case '\016':    /* ^N */
-       case 'j':       /* Vi-like alternative */
-           draw_cursor(1);
-           break;
-       case REDRAWKEY:
-           clear();
-           draw_screen();
-           break;
-       case KEY_HOME:
-               draw_cursor(-cur_part);
-               break;
-       case KEY_END:
-               draw_cursor(num_parts - cur_part - 1);
-               break;
-       default:
-           print_warning(_("Illegal command"));
-           putchar(BELL); /* CTRL-G */
-       }
-    }
+       fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf);
+       fdisk_context_enable_freespace(cf->cxt, 1);
 
-    die_x(0);
-}
+       if (argc != 2)
+               err(EXIT_FAILURE, "usage: %s <device>", argv[0]);
 
-static void
-copyright(void) {
-    fprintf(stderr, _("Copyright (C) 1994-2002 Kevin E. Martin & aeb\n"));
-}
+       if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0)
+               err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
 
+       cols_init(cf);
 
-static void __attribute__ ((__noreturn__)) usage(FILE *out)
-{
-    fputs(USAGE_HEADER, out);
-    fprintf(out, _(" %s [options] <device>\n"), program_invocation_short_name);
-    fputs(USAGE_OPTIONS, out);
-    fputs(_(" -c, --cylinders <number>  set the number of cylinders to use\n"), out);
-    fputs(_(" -h, --heads <number>      set the number of heads to use\n"), out);
-    fputs(_(" -s, --sectors <number>    set the number of sectors to use\n"), out);
-    fputs(_(" -g, --guess               guess a geometry from partition table\n"), out);
-    fputs(_(" -P, --print <r|s|t>       print partition table in specified format\n"), out);
-    fputs(_(" -z, --zero                start with zeroed partition table\n"), out);
-    fputs(_(" -a, --arrow               use arrow for highlighting the current partition\n"), out);
-    fputs(USAGE_SEPARATOR, out);
-    fputs(_("     --help     display this help and exit\n"), out);
-    fputs(USAGE_VERSION, out);
-    fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
-    copyright();
-    exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
-}
+       if (lines_refresh_buffer(cf))
+               errx(EXIT_FAILURE, _("failed to read partitions"));
 
-int
-main(int argc, char **argv)
-{
-    int c;
-    int i, len;
-
-    enum {
-       OPT_HELP = CHAR_MAX + 1,
-    };
-
-    static const struct option longopts[] = {
-       {"cylinders", required_argument, 0, 'c'},
-       {"heads", required_argument, 0, 'h'},
-       {"sectors", required_argument, 0, 's'},
-       {"guess", no_argument, 0, 'g'},
-       {"print", required_argument, 0, 'P'},
-       {"zero", no_argument, 0, 'z'},
-       {"arrow", no_argument, 0, 'a'},
-       {"help", no_argument, 0, OPT_HELP},
-       {"version", no_argument, 0, 'V'},
-       {NULL, no_argument, 0, '0'},
-    };
-
-    setlocale(LC_ALL, "");
-    bindtextdomain(PACKAGE, LOCALEDIR);
-    textdomain(PACKAGE);
-    atexit(close_stdout);
-
-    while ((c = getopt_long(argc, argv, "ac:gh:s:vVzP:", longopts, NULL)) != -1)
-       switch (c) {
-       case 'a':
-           arrow_cursor = TRUE;
-           break;
-       case 'c':
-               user_cylinders = cylinders = strtos64_or_err(optarg, _("cannot parse number of cylinders"));
-           if (cylinders <= 0) {
-               fprintf(stderr, "%s: %s\n", argv[0], _("Illegal cylinders value"));
-               exit(1);
-           }
-           break;
-       case 'g':
-           use_partition_table_geometry = TRUE;
-           break;
-       case 'h':
-           user_heads = heads = strtol_or_err(optarg, _("cannot parse number of heads"));
-           if (heads <= 0 || heads > MAX_HEADS) {
-               fprintf(stderr, "%s: %s\n", argv[0], _("Illegal heads value"));
-               exit(1);
-           }
-           break;
-       case 's':
-           user_sectors = sectors = strtol_or_err(optarg, _("cannot parse number of sectors"));
-           if (sectors <= 0 || sectors > MAX_SECTORS) {
-               fprintf(stderr, "%s: %s\n", argv[0], _("Illegal sectors value"));
-               exit(1);
-           }
-           break;
-       case 'v':
-       case 'V':
-           printf(UTIL_LINUX_VERSION);
-           copyright();
-           return EXIT_SUCCESS;
-       case 'z':
-           zero_table = TRUE;
-           break;
-       case 'P':
-           len = strlen(optarg);
-           for (i = 0; i < len; i++) {
-               switch (optarg[i]) {
-               case 'r':
-                   print_only |= PRINT_RAW_TABLE;
-                   break;
-               case 's':
-                   print_only |= PRINT_SECTOR_TABLE;
-                   break;
-               case 't':
-                   print_only |= PRINT_PARTITION_TABLE;
-                   break;
-               default:
-                   usage(stderr);
-               }
-           }
-           break;
-       case OPT_HELP:
-           usage(stdout);
-       default:
-           usage(stderr);
-       }
+       /* Don't use err(), warn() from this point */
+       ui_init(cf);
+       ui_run(cf);
+       ui_end(cf);
 
-    if (argc-optind == 1)
-       disk_device = argv[optind];
-    else if (argc-optind != 0) {
-       usage(stderr);
-    } else if ((fd = open(DEFAULT_DEVICE, O_RDONLY)) < 0)
-       disk_device = ALTERNATE_DEVICE;
-    else close(fd);
-
-    if (print_only) {
-       fill_p_info();
-       if (print_only & PRINT_RAW_TABLE)
-           print_raw_table();
-       if (print_only & PRINT_SECTOR_TABLE)
-           print_p_info();
-       if (print_only & PRINT_PARTITION_TABLE)
-           print_part_table();
-    } else
-       do_curses_fdisk();
-
-    return 0;
+       free(cf->lines);
+       free(cf->linesbuf);
+       fdisk_unref_table(cf->table);
+       fdisk_free_context(cf->cxt);
+       return EXIT_SUCCESS;
 }