util-linux/fdisks/fdisk-ask.c

275 lines
6.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"
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;
}
#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', <%jd,%jd>, default=%jd, 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 %jd): "),
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 (%jd-%jd, default %jd): "),
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 (%jd-%jd): "),
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);
printf(_("Value out of range.\n"));
} 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', <%jd,%jd>, base=%jd, default=%jd, range: %s]",
q, low, high, base, dflt, range));
if (range && dflt >= low && dflt <= high)
snprintf(prompt, sizeof(prompt), _("%s (%s, default %jd): "), q, range, dflt);
else if (dflt >= low && dflt <= high)
snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd, default %jd): "), q, low, high, dflt);
else
snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd): "), 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: %jd", 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: %jd [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);
}
printf(_("Value out of range.\n"));
} while (1);
return -1;
}
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);
switch(fdisk_ask_get_type(ask)) {
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:
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;
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_TABLE:
tt_print_table(fdisk_ask_get_table(ask));
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;
}