cfdisk: add UI for linfdisk menus, ask for size
Signed-off-by: Karel Zak <kzak@redhat.com>
This commit is contained in:
parent
5f3504f8c0
commit
b1f583304f
1 changed files with 308 additions and 9 deletions
317
fdisks/cfdisk.c
317
fdisks/cfdisk.c
|
@ -43,6 +43,16 @@
|
|||
#define TABLE_START_LINE 4
|
||||
#define MENU_START_LINE (LINES - 5)
|
||||
#define INFO_LINE (LINES - 2)
|
||||
#define HINT_LINE (LINES - 1)
|
||||
|
||||
#define CFDISK_ERR_ESC 5000
|
||||
|
||||
#ifndef KEY_ESC
|
||||
# define KEY_ESC '\033'
|
||||
#endif
|
||||
#ifndef KEY_DELETE
|
||||
# define KEY_DELETE '\177'
|
||||
#endif
|
||||
|
||||
/* colors */
|
||||
enum {
|
||||
|
@ -60,11 +70,17 @@ typedef int (menu_callback_t)(struct cfdisk *, int);
|
|||
static int menu_cb_main(struct cfdisk *cf, int key);
|
||||
static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx);
|
||||
static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
|
||||
static struct cfdisk_menu *menu_push(struct cfdisk *cf, size_t id, struct cfdisk_menudesc *desc);
|
||||
static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
|
||||
|
||||
static int ui_refresh(struct cfdisk *cf);
|
||||
static void ui_warnx(const char *fmt, ...);
|
||||
static void ui_warn(const char *fmt, ...);
|
||||
static void ui_info(const char *fmt, ...);
|
||||
static void ui_draw_menu(struct cfdisk *cf);
|
||||
static void ui_menu_goto(struct cfdisk *cf, int where);
|
||||
static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
|
||||
uintmax_t low, uintmax_t up);
|
||||
|
||||
static int ui_enabled;
|
||||
|
||||
|
@ -102,7 +118,11 @@ static struct cfdisk_menudesc menu_main[] = {
|
|||
};
|
||||
|
||||
enum {
|
||||
CFDISK_MENU_GENERATED = -1, /* used in libfdisk callback */
|
||||
|
||||
/* built-in menus */
|
||||
CFDISK_MENU_MAIN = 0,
|
||||
|
||||
};
|
||||
|
||||
static struct cfdisk_menudesc *menus[] = {
|
||||
|
@ -266,6 +286,70 @@ static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
|
|||
return fdisk_table_get_partition(cf->table, cf->lines_idx);
|
||||
}
|
||||
|
||||
/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
|
||||
* responseback to libfdisk
|
||||
*/
|
||||
static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
|
||||
{
|
||||
struct cfdisk_menudesc *d, *cm;
|
||||
int key;
|
||||
size_t i = 0, nitems;
|
||||
const char *name, *desc;
|
||||
|
||||
assert(ask);
|
||||
assert(cf);
|
||||
|
||||
/* create cfdisk menu according to libfdisk ask-menu, note that the
|
||||
* last cm[] item has to be empty -- so nitems + 1 */
|
||||
nitems = fdisk_ask_menu_get_nitems(ask);
|
||||
cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
|
||||
if (!cm)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < nitems; i++) {
|
||||
if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
|
||||
break;
|
||||
cm[i].key = key;
|
||||
cm[i].desc = desc;
|
||||
cm[i].name = name;
|
||||
}
|
||||
|
||||
/* make the new menu active */
|
||||
menu_push(cf, CFDISK_MENU_GENERATED, cm);
|
||||
ui_draw_menu(cf);
|
||||
refresh();
|
||||
|
||||
/* wait for keys */
|
||||
do {
|
||||
switch (getch()) {
|
||||
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':
|
||||
d = menu_get_menuitem(cf, cf->menu_idx);
|
||||
if (d)
|
||||
fdisk_ask_menu_set_result(ask, d->key);
|
||||
menu_pop(cf);
|
||||
free(cm);
|
||||
return 0;
|
||||
}
|
||||
} while (1);
|
||||
|
||||
menu_pop(cf);
|
||||
free(cm);
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
||||
static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
|
||||
void *data __attribute__((__unused__)))
|
||||
{
|
||||
|
@ -284,6 +368,9 @@ static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
|
|||
case FDISK_ASKTYPE_WARN:
|
||||
ui_warn(fdisk_ask_print_get_mesg(ask));
|
||||
break;
|
||||
case FDISK_ASKTYPE_MENU:
|
||||
ask_menu(ask, (struct cfdisk *) data);
|
||||
break;
|
||||
default:
|
||||
ui_warnx(_("internal error: unsupported dialog type %d"),
|
||||
fdisk_ask_get_type(ask));
|
||||
|
@ -320,7 +407,7 @@ static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap)
|
|||
|
||||
xvasprintf(&buf, fmt, ap);
|
||||
|
||||
width = strlen(buf); /* TODO: count cells! */
|
||||
width = mbs_safe_width(buf);
|
||||
|
||||
attron(attrs);
|
||||
mvaddstr(line, (COLS - width) / 2, buf);
|
||||
|
@ -380,6 +467,23 @@ static void ui_clean_info(void)
|
|||
clrtoeol();
|
||||
}
|
||||
|
||||
static void ui_hint(const char *fmt, ...)
|
||||
{
|
||||
va_list ap;
|
||||
va_start(ap, fmt);
|
||||
if (ui_enabled)
|
||||
ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
|
||||
else
|
||||
vfprintf(stdout, fmt, ap);
|
||||
va_end(ap);
|
||||
}
|
||||
|
||||
static void ui_clean_hint(void)
|
||||
{
|
||||
move(HINT_LINE, 0);
|
||||
clrtoeol();
|
||||
}
|
||||
|
||||
static void die_on_signal(int dummy __attribute__((__unused__)))
|
||||
{
|
||||
ui_end(NULL);
|
||||
|
@ -446,24 +550,26 @@ static void menu_update_ignore(struct cfdisk *cf)
|
|||
cf->menu_idx = 0;
|
||||
}
|
||||
|
||||
static struct cfdisk_menu *menu_push(struct cfdisk *cf, size_t id)
|
||||
static struct cfdisk_menu *menu_push(
|
||||
struct cfdisk *cf,
|
||||
size_t id,
|
||||
struct cfdisk_menudesc *desc)
|
||||
{
|
||||
struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
|
||||
struct cfdisk_menudesc *d;
|
||||
|
||||
assert(cf);
|
||||
assert(id < ARRAY_SIZE(menus));
|
||||
|
||||
DBG(FRONTEND, dbgprint("menu: new menu"));
|
||||
|
||||
m->prev = cf->menu;
|
||||
m->id = id;
|
||||
m->desc = menus[id];
|
||||
m->desc = desc ? desc : menus[id];
|
||||
m->callback = menu_callbacks[id];
|
||||
|
||||
for (d = m->desc; d->name; d++) {
|
||||
const char *name = _(d->name);
|
||||
size_t len = strlen(name); /* TODO: we care about cells! */
|
||||
size_t len = mbs_safe_width(name);
|
||||
if (len > m->width)
|
||||
m->width = len;
|
||||
m->nitems++;
|
||||
|
@ -494,14 +600,16 @@ static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
|
|||
static int menu_cb_main(struct cfdisk *cf, int key)
|
||||
{
|
||||
size_t n;
|
||||
int ref = 0;
|
||||
int ref = 0, rc;
|
||||
const char *info = NULL, *warn = NULL;
|
||||
struct fdisk_partition *pa;
|
||||
|
||||
assert(cf);
|
||||
assert(cf->cxt);
|
||||
assert(key);
|
||||
|
||||
n = cf->lines_idx; /* the current partition */
|
||||
pa = get_current_partition(cf);
|
||||
|
||||
switch (key) {
|
||||
case 'b': /* Bootable flag */
|
||||
|
@ -523,7 +631,34 @@ static int menu_cb_main(struct cfdisk *cf, int key)
|
|||
ref = 1;
|
||||
break;
|
||||
case 'n': /* New */
|
||||
{
|
||||
uint64_t start, size;
|
||||
struct fdisk_partition *npa; /* the new partition */
|
||||
|
||||
if (!pa || !fdisk_partition_is_freespace(pa))
|
||||
return -EINVAL;
|
||||
npa = fdisk_new_partition();
|
||||
if (!npa)
|
||||
return -ENOMEM;
|
||||
/* free space range */
|
||||
start = fdisk_partition_get_start(pa);
|
||||
size = fdisk_partition_get_size(pa) * cf->cxt->sector_size;
|
||||
|
||||
if (ui_get_size(cf, _("Partition size: "), &size, 1, size)
|
||||
== -CFDISK_ERR_ESC)
|
||||
break;
|
||||
size /= cf->cxt->sector_size;
|
||||
/* properties of the new partition */
|
||||
fdisk_partition_set_start(npa, start);
|
||||
fdisk_partition_set_size(npa, size);
|
||||
fdisk_partition_partno_follow_default(npa, 1);
|
||||
/* add to disk label -- libfdisk will ask for missing details */
|
||||
rc = fdisk_add_partition(cf->cxt, npa);
|
||||
fdisk_unref_partition(npa);
|
||||
if (rc == 0)
|
||||
ref = 1;
|
||||
break;
|
||||
}
|
||||
case 'q': /* Quit */
|
||||
return 1;
|
||||
case 't': /* Type */
|
||||
|
@ -544,7 +679,7 @@ static int menu_cb_main(struct cfdisk *cf, int key)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ui_init(struct cfdisk *cf)
|
||||
static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
|
||||
{
|
||||
struct sigaction sa;
|
||||
|
||||
|
@ -656,7 +791,7 @@ static void ui_draw_menuitem(struct cfdisk *cf,
|
|||
mvprintw(ln, cl, "[%s]", buf);
|
||||
standend();
|
||||
if (d->desc)
|
||||
ui_center(LINES - 1, d->desc);
|
||||
ui_hint(d->desc);
|
||||
} else
|
||||
mvprintw(ln, cl, "[%s]", buf);
|
||||
}
|
||||
|
@ -832,13 +967,177 @@ static int ui_refresh(struct cfdisk *cf)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
|
||||
const char *hint, char *buf, size_t len)
|
||||
{
|
||||
size_t cells = 0;
|
||||
ssize_t i = 0, rc = -1;
|
||||
wint_t c;
|
||||
int ln = MENU_START_LINE, cl = 1;
|
||||
|
||||
assert(cf);
|
||||
assert(buf);
|
||||
assert(len);
|
||||
|
||||
move(ln, 0);
|
||||
clrtoeol();
|
||||
|
||||
if (prompt) {
|
||||
mvaddstr(ln, cl, prompt);
|
||||
cl += mbs_safe_width(prompt);
|
||||
}
|
||||
|
||||
/* default value */
|
||||
if (*buf) {
|
||||
i = strlen(buf);
|
||||
cells = mbs_safe_width(buf);
|
||||
mvaddstr(ln, cl, buf);
|
||||
}
|
||||
|
||||
if (hint)
|
||||
ui_hint(hint);
|
||||
else
|
||||
ui_clean_hint();
|
||||
|
||||
move(ln, cl + cells);
|
||||
curs_set(1);
|
||||
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
|
||||
goto done;
|
||||
}
|
||||
if (c == '\r' || c == '\n' || c == KEY_ENTER)
|
||||
break;
|
||||
|
||||
switch (c) {
|
||||
case KEY_ESC:
|
||||
rc = -CFDISK_ERR_ESC;
|
||||
goto done;
|
||||
case KEY_DELETE:
|
||||
case '\b':
|
||||
case KEY_BACKSPACE:
|
||||
if (i > 0) {
|
||||
cells--;
|
||||
i = mbs_truncate(buf, &cells);
|
||||
if (i < 0)
|
||||
goto done;
|
||||
mvaddch(ln, cl + cells, ' ');
|
||||
move(ln, cl + cells);
|
||||
} else
|
||||
beep();
|
||||
break;
|
||||
default:
|
||||
#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
|
||||
if (i + 1 < (ssize_t) 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 < (ssize_t) len) {
|
||||
s[sz] = '\0';
|
||||
mvaddnstr(ln, cl + cells, s, sz);
|
||||
memcpy(buf + i, s, sz);
|
||||
i += sz;
|
||||
buf[i] = '\0';
|
||||
cells += wcwidth(wc);
|
||||
} else
|
||||
beep();
|
||||
}
|
||||
#else
|
||||
if (i + 1 < (ssize_t) len && isprint(c)) {
|
||||
mvaddch(ln, cl + cells, c);
|
||||
str[i++] = c;
|
||||
str[i] = '\0';
|
||||
cells++;
|
||||
}
|
||||
#endif
|
||||
else
|
||||
beep();
|
||||
}
|
||||
refresh();
|
||||
}
|
||||
|
||||
rc = i; /* success */
|
||||
done:
|
||||
move(ln, 0);
|
||||
clrtoeol();
|
||||
curs_set(0);
|
||||
refresh();
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/* @res is default value as well as result in bytes */
|
||||
static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
|
||||
uintmax_t low, uintmax_t up)
|
||||
{
|
||||
char buf[128];
|
||||
uintmax_t user = 0;
|
||||
ssize_t rc;
|
||||
char *dflt = size_to_human_string(0, *res);
|
||||
|
||||
DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res));
|
||||
|
||||
ui_clean_info();
|
||||
|
||||
do {
|
||||
int pwr = 0;
|
||||
|
||||
snprintf(buf, sizeof(buf), "%s", dflt);
|
||||
rc = ui_get_string(cf, prompt,
|
||||
_("The size may be followed by MiB, GiB or TiB suffix "
|
||||
"(the \"iB\" is optional)."),
|
||||
buf, sizeof(buf));
|
||||
if (rc == 0) {
|
||||
ui_warnx(_("Please, specify size."));
|
||||
continue; /* nothing specified */
|
||||
} else if (rc == -CFDISK_ERR_ESC)
|
||||
break; /* cancel dialog */
|
||||
|
||||
rc = parse_size(buf, &user, &pwr); /* response, parse */
|
||||
if (rc == 0) {
|
||||
DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d", user, pwr));
|
||||
if (user < low) {
|
||||
ui_warnx(_("Minimal size is %ju"), low);
|
||||
rc = -ERANGE;
|
||||
}
|
||||
if (user > up && pwr && user < up + (1ULL << pwr * 10))
|
||||
/* ignore when the user specified size overflow
|
||||
* with in range specified by suffix (e.g. MiB) */
|
||||
user = up;
|
||||
|
||||
if (user > up) {
|
||||
ui_warnx(_("Maximal size is %ju bytes."), up);
|
||||
rc = -ERANGE;
|
||||
}
|
||||
}
|
||||
} while (rc != 0);
|
||||
|
||||
if (rc == 0)
|
||||
*res = user;
|
||||
free(dflt);
|
||||
|
||||
DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc));
|
||||
return rc;
|
||||
}
|
||||
|
||||
|
||||
static int ui_run(struct cfdisk *cf)
|
||||
{
|
||||
int rc;
|
||||
|
||||
DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES));
|
||||
|
||||
menu_push(cf, CFDISK_MENU_MAIN);
|
||||
menu_push(cf, CFDISK_MENU_MAIN, NULL);
|
||||
|
||||
rc = ui_refresh(cf);
|
||||
if (rc)
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue