373 lines
8.8 KiB
C
373 lines
8.8 KiB
C
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <ctype.h>
|
|
#include <stdint.h>
|
|
|
|
#include "c.h"
|
|
#include "strutils.h"
|
|
#include "rpmatch.h"
|
|
#include "xalloc.h"
|
|
|
|
#include "fdisk.h"
|
|
|
|
static unsigned int info_count;
|
|
|
|
int get_user_reply(struct fdisk_context *cxt, const char *prompt,
|
|
char *buf, size_t bufsz)
|
|
{
|
|
char *p;
|
|
size_t sz;
|
|
|
|
do {
|
|
fputs(prompt, stdout);
|
|
fflush(stdout);
|
|
|
|
if (!fgets(buf, bufsz, stdin)) {
|
|
if (fdisk_label_is_changed(cxt->label)) {
|
|
fprintf(stderr, _("\nDo you really want to quit? "));
|
|
|
|
if (fgets(buf, bufsz, stdin) && !rpmatch(buf))
|
|
continue;
|
|
}
|
|
fdisk_free_context(cxt);
|
|
exit(EXIT_FAILURE);
|
|
} else
|
|
break;
|
|
} while (1);
|
|
|
|
for (p = buf; *p && !isgraph(*p); p++); /* get first non-blank */
|
|
|
|
if (p > buf)
|
|
memmove(buf, p, p - buf); /* remove blank space */
|
|
sz = strlen(buf);
|
|
if (sz && *(buf + sz - 1) == '\n')
|
|
*(buf + sz - 1) = '\0';
|
|
|
|
DBG(ASK, dbgprint("user's reply: >>>%s<<<", buf));
|
|
return 0;
|
|
}
|
|
|
|
static int ask_menu(struct fdisk_context *cxt, struct fdisk_ask *ask,
|
|
char *buf, size_t bufsz)
|
|
|
|
{
|
|
const char *q = fdisk_ask_get_query(ask);
|
|
int dft = fdisk_ask_menu_get_default(ask);
|
|
|
|
if (q) {
|
|
fputs(q, stdout); /* print header */
|
|
fputc('\n', stdout);
|
|
}
|
|
|
|
do {
|
|
char prompt[128];
|
|
int key, c;
|
|
const char *name, *desc;
|
|
size_t i = 0;
|
|
|
|
/* print menu items */
|
|
while (fdisk_ask_menu_get_item(ask, i++, &key, &name, &desc) == 0)
|
|
fprintf(stdout, " %c %s (%s)\n", key, name, desc);
|
|
|
|
/* ask for key */
|
|
snprintf(prompt, sizeof(prompt), _("Select (default %c): "), dft);
|
|
get_user_reply(cxt, prompt, buf, bufsz);
|
|
if (!*buf) {
|
|
fdisk_info(cxt, _("Using default response %c."), dft);
|
|
c = dft;
|
|
} else
|
|
c = tolower(buf[0]);
|
|
|
|
/* check result */
|
|
i = 0;
|
|
while (fdisk_ask_menu_get_item(ask, i++, &key, NULL, NULL) == 0) {
|
|
if (c == key) {
|
|
fdisk_ask_menu_set_result(ask, c);
|
|
return 0; /* success */
|
|
}
|
|
}
|
|
fdisk_warnx(cxt, _("Value out of range."));
|
|
} while (1);
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
|
|
#define tochar(num) ((int) ('a' + num - 1))
|
|
static int ask_number(struct fdisk_context *cxt,
|
|
struct fdisk_ask *ask,
|
|
char *buf, size_t bufsz)
|
|
{
|
|
char prompt[128] = { '\0' };
|
|
const char *q = fdisk_ask_get_query(ask);
|
|
const char *range = fdisk_ask_number_get_range(ask);
|
|
|
|
uint64_t dflt = fdisk_ask_number_get_default(ask),
|
|
low = fdisk_ask_number_get_low(ask),
|
|
high = fdisk_ask_number_get_high(ask);
|
|
int inchar = fdisk_ask_number_inchars(ask);
|
|
|
|
assert(q);
|
|
|
|
DBG(ASK, dbgprint("asking for number "
|
|
"['%s', <%ju,%ju>, default=%ju, range: %s]",
|
|
q, low, high, dflt, range));
|
|
|
|
if (range && dflt >= low && dflt <= high) {
|
|
if (inchar)
|
|
snprintf(prompt, sizeof(prompt), _("%s (%s, default %c): "),
|
|
q, range, tochar(dflt));
|
|
else
|
|
snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "),
|
|
q, range, dflt);
|
|
|
|
} else if (dflt >= low && dflt <= high) {
|
|
if (inchar)
|
|
snprintf(prompt, sizeof(prompt), _("%s (%c-%c, default %c): "),
|
|
q, tochar(low), tochar(high), tochar(dflt));
|
|
else
|
|
snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "),
|
|
q, low, high, dflt);
|
|
} else if (inchar)
|
|
snprintf(prompt, sizeof(prompt), _("%s (%c-%c): "),
|
|
q, tochar(low), tochar(high));
|
|
else
|
|
snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "),
|
|
q, low, high);
|
|
|
|
do {
|
|
int rc = get_user_reply(cxt, prompt, buf, bufsz);
|
|
uint64_t num;
|
|
|
|
if (rc)
|
|
return rc;
|
|
if (!*buf && dflt >= low && dflt <= high)
|
|
return fdisk_ask_number_set_result(ask, dflt);
|
|
|
|
if (isdigit_string(buf)) {
|
|
char *end;
|
|
|
|
errno = 0;
|
|
num = strtoumax(buf, &end, 10);
|
|
if (errno || buf == end || (end && *end))
|
|
continue;
|
|
} else if (inchar && isalpha(*buf)) {
|
|
num = tolower(*buf) - 'a' + 1;
|
|
} else
|
|
rc = -EINVAL;
|
|
|
|
if (rc == 0 && num >= low && num <= high)
|
|
return fdisk_ask_number_set_result(ask, num);
|
|
|
|
fdisk_warnx(cxt, _("Value out of range."));
|
|
} while (1);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static int ask_offset(struct fdisk_context *cxt,
|
|
struct fdisk_ask *ask,
|
|
char *buf, size_t bufsz)
|
|
{
|
|
char prompt[128] = { '\0' };
|
|
const char *q = fdisk_ask_get_query(ask);
|
|
const char *range = fdisk_ask_number_get_range(ask);
|
|
|
|
uint64_t dflt = fdisk_ask_number_get_default(ask),
|
|
low = fdisk_ask_number_get_low(ask),
|
|
high = fdisk_ask_number_get_high(ask),
|
|
base = fdisk_ask_number_get_base(ask);
|
|
|
|
assert(q);
|
|
|
|
DBG(ASK, dbgprint("asking for offset ['%s', <%ju,%ju>, base=%ju, default=%ju, range: %s]",
|
|
q, low, high, base, dflt, range));
|
|
|
|
if (range && dflt >= low && dflt <= high)
|
|
snprintf(prompt, sizeof(prompt), _("%s (%s, default %ju): "), q, range, dflt);
|
|
else if (dflt >= low && dflt <= high)
|
|
snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju, default %ju): "), q, low, high, dflt);
|
|
else
|
|
snprintf(prompt, sizeof(prompt), _("%s (%ju-%ju): "), q, low, high);
|
|
|
|
do {
|
|
uint64_t num = 0;
|
|
char sig = 0, *p;
|
|
int pwr = 0;
|
|
|
|
int rc = get_user_reply(cxt, prompt, buf, bufsz);
|
|
if (rc)
|
|
return rc;
|
|
if (!*buf && dflt >= low && dflt <= high)
|
|
return fdisk_ask_number_set_result(ask, dflt);
|
|
|
|
p = buf;
|
|
if (*p == '+' || *p == '-') {
|
|
sig = *buf;
|
|
p++;
|
|
}
|
|
|
|
rc = parse_size(p, &num, &pwr);
|
|
if (rc)
|
|
continue;
|
|
DBG(ASK, dbgprint("parsed size: %ju", num));
|
|
if (sig && pwr) {
|
|
/* +{size}{K,M,...} specified, the "num" is in bytes */
|
|
uint64_t unit = fdisk_ask_number_get_unit(ask);
|
|
num += unit/2; /* round */
|
|
num /= unit;
|
|
}
|
|
if (sig == '+')
|
|
num += base;
|
|
else if (sig == '-')
|
|
num = base - num;
|
|
|
|
DBG(ASK, dbgprint("final offset: %ju [sig: %c, power: %d, %s]",
|
|
num, sig, pwr,
|
|
sig ? "relative" : "absolute"));
|
|
if (num >= low && num <= high) {
|
|
if (sig)
|
|
fdisk_ask_number_set_relative(ask, 1);
|
|
return fdisk_ask_number_set_result(ask, num);
|
|
}
|
|
fdisk_warnx(cxt, _("Value out of range."));
|
|
} while (1);
|
|
|
|
return -1;
|
|
}
|
|
|
|
static void fputs_info(struct fdisk_ask *ask, FILE *out, char *buf, size_t bufsz)
|
|
{
|
|
const char *msg;
|
|
unsigned int flags;
|
|
|
|
assert(ask);
|
|
|
|
msg = fdisk_ask_print_get_mesg(ask);
|
|
flags = fdisk_ask_get_flags(ask);
|
|
|
|
if (!msg)
|
|
return;
|
|
if (info_count == 1)
|
|
fputc('\n', out);
|
|
if (flags == 0 || !colors_wanted())
|
|
goto simple;
|
|
|
|
if (flags & FDISK_INFO_COLON) {
|
|
size_t sz;
|
|
char *sep = _(": ");
|
|
char *p = strstr(msg, sep);
|
|
|
|
if (!p)
|
|
goto simple;
|
|
|
|
sz = strlen(sep);
|
|
strncpy(buf, msg, bufsz);
|
|
buf[p - msg + sz] = '\0';
|
|
|
|
color_enable(UL_COLOR_BROWN);
|
|
fputs(buf, out);
|
|
color_disable();
|
|
fputs(p + sz, out);
|
|
|
|
} else if (flags & FDISK_INFO_SUCCESS) {
|
|
color_enable(UL_COLOR_BOLD);
|
|
fputs(msg, out);
|
|
color_disable();
|
|
} else {
|
|
simple:
|
|
fputs(msg, out);
|
|
}
|
|
fputc('\n', out);
|
|
}
|
|
|
|
int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
|
|
void *data __attribute__((__unused__)))
|
|
{
|
|
int rc = 0;
|
|
char buf[BUFSIZ];
|
|
|
|
assert(cxt);
|
|
assert(ask);
|
|
|
|
if (fdisk_ask_get_type(ask) != FDISK_ASKTYPE_INFO)
|
|
info_count = 0;
|
|
|
|
switch(fdisk_ask_get_type(ask)) {
|
|
case FDISK_ASKTYPE_MENU:
|
|
return ask_menu(cxt, ask, buf, sizeof(buf));
|
|
case FDISK_ASKTYPE_NUMBER:
|
|
return ask_number(cxt, ask, buf, sizeof(buf));
|
|
case FDISK_ASKTYPE_OFFSET:
|
|
return ask_offset(cxt, ask, buf, sizeof(buf));
|
|
case FDISK_ASKTYPE_INFO:
|
|
info_count++;
|
|
fputs_info(ask, stdout, buf, sizeof(buf));
|
|
break;
|
|
case FDISK_ASKTYPE_WARNX:
|
|
color_fenable(UL_COLOR_RED, stderr);
|
|
fputs(fdisk_ask_print_get_mesg(ask), stderr);
|
|
color_fdisable(stderr);
|
|
fputc('\n', stderr);
|
|
break;
|
|
case FDISK_ASKTYPE_WARN:
|
|
color_fenable(UL_COLOR_RED, stderr);
|
|
fputs(fdisk_ask_print_get_mesg(ask), stderr);
|
|
errno = fdisk_ask_print_get_errno(ask);
|
|
fprintf(stderr, ": %m\n");
|
|
color_fdisable(stderr);
|
|
break;
|
|
case FDISK_ASKTYPE_YESNO:
|
|
fputc('\n', stdout);
|
|
fputs(fdisk_ask_get_query(ask), stdout);
|
|
rc = get_user_reply(cxt, _(" [Y]es/[N]o: "), buf, sizeof(buf));
|
|
if (rc == 0)
|
|
fdisk_ask_yesno_set_result(ask, rpmatch(buf));
|
|
DBG(ASK, dbgprint("yes-no ask: reply '%s' [rc=%d]", buf, rc));
|
|
break;
|
|
case FDISK_ASKTYPE_STRING:
|
|
{
|
|
char prmt[BUFSIZ];
|
|
snprintf(prmt, sizeof(prmt), "%s: ", fdisk_ask_get_query(ask));
|
|
fputc('\n', stdout);
|
|
rc = get_user_reply(cxt, prmt, buf, sizeof(buf));
|
|
if (rc == 0)
|
|
fdisk_ask_string_set_result(ask, xstrdup(buf));
|
|
DBG(ASK, dbgprint("string ask: reply '%s' [rc=%d]", buf, rc));
|
|
break;
|
|
}
|
|
default:
|
|
warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask));
|
|
return -EINVAL;
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
struct fdisk_parttype *ask_partition_type(struct fdisk_context *cxt)
|
|
{
|
|
const char *q;
|
|
|
|
if (!cxt || !cxt->label || !cxt->label->nparttypes)
|
|
return NULL;
|
|
|
|
q = fdisk_is_parttype_string(cxt) ?
|
|
_("Partition type (type L to list all types): ") :
|
|
_("Hex code (type L to list all codes): ");
|
|
do {
|
|
char buf[256];
|
|
int rc = get_user_reply(cxt, q, buf, sizeof(buf));
|
|
|
|
if (rc)
|
|
break;
|
|
|
|
if (buf[1] == '\0' && toupper(*buf) == 'L')
|
|
list_partition_types(cxt);
|
|
else if (*buf)
|
|
return fdisk_parse_parttype(cxt, buf);
|
|
} while (1);
|
|
|
|
return NULL;
|
|
}
|