From: Karel Zak Date: Tue, 29 Jan 2013 14:28:30 +0000 (+0100) Subject: libfdisk: extend "ask" API, add support for offsets X-Git-Tag: v2.23-rc1~120 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=4114da08d090dcf02d77c8e6ed343b40d9c8ff96;p=thirdparty%2Futil-linux.git libfdisk: extend "ask" API, add support for offsets Signed-off-by: Karel Zak --- diff --git a/fdisks/fdisk-ask.c b/fdisks/fdisk-ask.c index ebfb404bb2..c27deae76d 100644 --- a/fdisks/fdisk-ask.c +++ b/fdisks/fdisk-ask.c @@ -51,40 +51,40 @@ static int ask_number(struct fdisk_context *cxt, char *buf, size_t bufsz) { char prompt[128] = { '\0' }; - const char *q = fdisk_ask_get_question(ask); + const char *q = fdisk_ask_get_query(ask); const char *range = fdisk_ask_number_get_range(ask); - uint64_t dfl = fdisk_ask_number_get_default(ask), + uint64_t dflt = fdisk_ask_number_get_default(ask), low = fdisk_ask_number_get_low(ask), - hig = fdisk_ask_number_get_high(ask); + high = fdisk_ask_number_get_high(ask); assert(q); - DBG(ASK, dbgprint("asking for number ['%s', <%jd,%jd>, default: %jd, range: %s]", - q, low, hig, dfl, range)); - - if (range && dfl) - snprintf(prompt, sizeof(prompt), _("%s (%s, default %jd): "), q, range, dfl); - else if (dfl) - snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd, default %jd): "), q, low, hig, dfl); + DBG(ASK, dbgprint("asking for number ['%s', <%jd,%jd>, default=%jd, range: %s]", + q, low, high, dflt, range)); + if (range && dflt) + snprintf(prompt, sizeof(prompt), _("%s (%s, default %jd): "), q, range, dflt); + else if (dflt) + snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd, default %jd): "), q, low, high, dflt); else - snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd): "), q, low, hig); + snprintf(prompt, sizeof(prompt), _("%s (%jd-%jd): "), q, low, high); do { - uint64_t num; int rc = get_user_reply(cxt, prompt, buf, bufsz); + if (rc) return rc; - - if (!*buf && dfl) - return fdisk_ask_number_set_result(ask, dfl); + if (!*buf && dflt) + return fdisk_ask_number_set_result(ask, dflt); else if (isdigit_string(buf)) { char *end; + uint64_t num; + errno = 0; num = strtoumax(buf, &end, 10); if (errno || buf == end || (end && *end)) continue; - if (num >= low && num <= hig) + if (num >= low && num <= high) return fdisk_ask_number_set_result(ask, num); printf(_("Value out of range.\n")); } @@ -93,6 +93,77 @@ static int ask_number(struct fdisk_context *cxt, 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) + snprintf(prompt, sizeof(prompt), _("%s (%s, default %jd): "), q, range, dflt); + else if (dflt) + 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) + 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__))) { @@ -104,7 +175,10 @@ int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *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)); default: + warnx(_("internal error: unssuported dialog type %d"), fdisk_ask_get_type(ask)); return -EINVAL; } return 0; diff --git a/libfdisk/src/ask.c b/libfdisk/src/ask.c index a5ee2f5591..fc0214d26e 100644 --- a/libfdisk/src/ask.c +++ b/libfdisk/src/ask.c @@ -1,42 +1,132 @@ +#include "strutils.h" + #include "fdiskP.h" +struct fdisk_ask *fdisk_new_ask(void) +{ + return calloc(1, sizeof(struct fdisk_ask)); +} + +void fdisk_reset_ask(struct fdisk_ask *ask) +{ + assert(ask); + free(ask->data.num.range); + free(ask->query); + memset(ask, 0, sizeof(*ask)); +} + +void fdisk_free_ask(struct fdisk_ask *ask) +{ + if (!ask) + return; + fdisk_reset_ask(ask); + free(ask); +} -const char *fdisk_ask_get_question(struct fdisk_ask *ask) +const char *fdisk_ask_get_query(struct fdisk_ask *ask) { assert(ask); return ask->query; } +int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str) +{ + assert(ask); + return !strdup_to_struct_member(ask, query, str) ? -ENOMEM : 0; +} + int fdisk_ask_get_type(struct fdisk_ask *ask) { assert(ask); return ask->type; } +int fdisk_ask_set_type(struct fdisk_ask *ask, int type) +{ + assert(ask); + ask->type = type; + return 0; +} + +int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask) +{ + int rc; + + assert(ask); + assert(cxt); + + DBG(ASK, dbgprint("asking for '%s'", ask->query)); + + if (!cxt->ask_cb) { + DBG(ASK, dbgprint("no ask callback specified!")); + return -EINVAL; + } + + rc = cxt->ask_cb(cxt, ask, cxt->ask_data); + + DBG(ASK, dbgprint("do_ask done [rc=%d]", rc)); + return rc; +} + + const char *fdisk_ask_number_get_range(struct fdisk_ask *ask) { assert(ask); return ask->data.num.range; } + +int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range) +{ + assert(ask); + return !strdup_to_struct_member(ask, data.num.range, range) ? -ENOMEM : 0; +} + uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask) { assert(ask); return ask->data.num.dfl; } +int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt) +{ + assert(ask); + ask->data.num.dfl = dflt; + return 0; +} + uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask) { assert(ask); return ask->data.num.low; } +int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low) +{ + assert(ask); + ask->data.num.low = low; + return 0; +} + uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask) { assert(ask); return ask->data.num.hig; } +int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high) +{ + assert(ask); + ask->data.num.hig = high; + return 0; +} + +uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.result; +} + int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result) { assert(ask); @@ -44,6 +134,46 @@ int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result) return 0; } +uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.base; +} + +int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base) +{ + assert(ask); + ask->data.num.base = base; + return 0; +} + +/* if numbers are not in bytes, then specify number of bytes per the unit */ +uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.unit; +} + +int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit) +{ + assert(ask); + ask->data.num.unit = unit; + return 0; +} + +int fdisk_ask_number_is_relative(struct fdisk_ask *ask) +{ + assert(ask); + return ask->data.num.relative; +} + +int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative) +{ + assert(ask); + ask->data.num.relative = relative ? 1 : 0; + return 0; +} + /* * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur' */ @@ -97,39 +227,41 @@ static char *mk_string_list(char *ptr, size_t *len, size_t *begin, return ptr; } +/* returns: 1=0 on success, < 0 on error, 1 if no free partition */ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) { - int rc; + int rc = 0; char range[BUFSIZ], *ptr = range; size_t i, len = sizeof(range), begin = 0, run = 0; - struct fdisk_ask ask = { .name = "partnum" }; - __typeof__(ask.data.num) *num = &ask.data.num; + struct fdisk_ask *ask = NULL; + __typeof__(ask->data.num) *num; assert(cxt); assert(cxt->label); assert(partnum); - if (!cxt->ask_cb) { - DBG(ASK, dbgprint("no ask callback specified!")); - return -EINVAL; /* ask callback undefined */ - } - DBG(ASK, dbgprint("%s: asking for %s partition number (max: %zd)", - cxt->label->name, - wantnew ? "new" : "used", + cxt->label->name, wantnew ? "new" : "used", cxt->label->nparts_max)); + ask = fdisk_new_ask(); + if (!ask) + return -ENOMEM; + + num = &ask->data.num; + for (i = 0; i < cxt->label->nparts_max; i++) { int status = 0; rc = fdisk_partition_get_status(cxt, i, &status); if (rc) - return rc; - + break; if (wantnew && !(status & FDISK_PARTSTAT_USED)) { ptr = mk_string_list(ptr, &len, &begin, &run, i); - if (!ptr) - return -EINVAL; + if (!ptr) { + rc = -EINVAL; + break; + } if (!num->low) num->dfl = num->low = i + 1; num->hig = i + 1; @@ -141,20 +273,28 @@ int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) } } - if (wantnew && num->low == 0 && num->hig == 0) { + if (!rc) { + mk_string_list(ptr, &len, &begin, &run, -1); /* terminate the list */ + rc = fdisk_ask_number_set_range(ask, range); + } + if (!rc && wantnew && num->low == 0 && num->hig == 0) { DBG(ASK, dbgprint("no free partition")); - return 1; + rc = 1; + } + if (!rc) + rc = fdisk_ask_set_query(ask, _("Partition number")); + if (!rc) + rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + if (!rc) + rc = fdisk_do_ask(cxt, ask); + if (!rc) { + *partnum = fdisk_ask_number_get_result(ask); + if (*partnum) + *partnum -= 1; } - mk_string_list(ptr, &len, &begin, &run, -1); /* terminate the list */ - num->range = range; - - ask.query = _("Partition number"); - ask.type = FDISK_ASKTYPE_NUMBER; - - rc = cxt->ask_cb(cxt, &ask, cxt->ask_data); - *partnum = num->result ? num->result - 1 : 0; - DBG(ASK, dbgprint("result: %zd [rc=%d]\n", num->result, rc)); + fdisk_free_ask(ask); + DBG(ASK, dbgprint("result: %zd [rc=%d]\n", *partnum, rc)); return rc; } @@ -167,20 +307,6 @@ struct fdisk_label *fdisk_new_mac_label(struct fdisk_context *cxt) { return NULL struct fdisk_label *fdisk_new_sgi_label(struct fdisk_context *cxt) { return NULL; } struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt) { return NULL; } -unsigned int read_int_with_suffix(struct fdisk_context *cxt, - unsigned int low, unsigned int dflt, unsigned int high, - unsigned int base, char *mesg, int *is_suffix_used) -{ - return 0; -} - -unsigned int read_int(struct fdisk_context *cxt, - unsigned int low, unsigned int dflt, - unsigned int high, unsigned int base, char *mesg) -{ - return 0; -} - int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) { /* 1 - 3, 6, 8, 9, 11 13 */ diff --git a/libfdisk/src/fdiskP.h b/libfdisk/src/fdiskP.h index 58c8393cc6..b1de7036c2 100644 --- a/libfdisk/src/fdiskP.h +++ b/libfdisk/src/fdiskP.h @@ -212,28 +212,24 @@ extern struct fdisk_label *fdisk_new_sun_label(struct fdisk_context *cxt); * we will have get/set() function for everything. */ struct fdisk_ask { - const char *name; int type; /* FDISK_ASKTYPE_* */ - char *query; - char *hint; union { + /* FDISK_ASKTYPE_{NUMBER,OFFSET} */ struct ask_number { uint64_t hig; /* high limit */ uint64_t low; /* low limit */ uint64_t dfl; /* default */ uint64_t result; - const char *range; /* by library generated list */ + uint64_t base; /* for relative results */ + uint64_t unit; /* unit for offsets */ + char *range; /* by library generated list */ + unsigned int relative:1; } num; } data; }; -enum { - FDISK_ASKTYPE_NONE, - FDISK_ASKTYPE_NUMBER -}; - struct fdisk_context { int dev_fd; /* device descriptor */ char *dev_path; /* device path */ diff --git a/libfdisk/src/gpt.c b/libfdisk/src/gpt.c index 520416c45c..278cb1853d 100644 --- a/libfdisk/src/gpt.c +++ b/libfdisk/src/gpt.c @@ -44,17 +44,6 @@ #include "strutils.h" #include "all-io.h" -/* temporary -- exported from fdisk/sfdisk.c - * TODO: use fdisk_dialog API - */ -extern unsigned int read_int(struct fdisk_context *cxt, - unsigned int low, unsigned int dflt, - unsigned int high, unsigned int base, char *mesg); - -extern unsigned int read_int_with_suffix(struct fdisk_context *cxt, - unsigned int low, unsigned int dflt, unsigned int high, - unsigned int base, char *mesg, int *is_suffix_used); - #define GPT_HEADER_SIGNATURE 0x5452415020494645LL /* EFI PART */ #define GPT_HEADER_REVISION_V1_02 0x00010200 #define GPT_HEADER_REVISION_V1_00 0x00010000 @@ -1564,6 +1553,8 @@ static int gpt_add_partition( struct fdisk_gpt_label *gpt; struct gpt_header *pheader; struct gpt_entry *ents; + struct fdisk_ask *ask = NULL; + int rc; assert(cxt); assert(cxt->label); @@ -1608,35 +1599,48 @@ static int gpt_add_partition( /* get user input for first and last sectors of the new partition */ for (;;) { - int is_suffix_used = 0; - - /* first sector */ - user_f = read_int(cxt, disk_f, /* minimal */ - dflt_f, /* default */ - disk_l, /* maximal */ - 0, _("First sector")); - - if (user_f < disk_f || user_f > disk_l) - continue; /* bug in read_int() dialog? */ - + if (!ask) + ask = fdisk_new_ask(); + else + fdisk_reset_ask(ask); + + /* First sector */ + fdisk_ask_set_query(ask, _("First sector")); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); + fdisk_ask_number_set_low(ask, disk_f); /* minimal */ + fdisk_ask_number_set_default(ask, dflt_f); /* default */ + fdisk_ask_number_set_high(ask, disk_l); /* maximal */ + + rc = fdisk_do_ask(cxt, ask); + if (rc) + goto done; + + user_f = fdisk_ask_number_get_result(ask); if (user_f != find_first_available(pheader, ents, user_f)) { printf(_("Sector %ju already used\n"), user_f); continue; } + fdisk_reset_ask(ask); + /* Last sector */ dflt_l = find_last_free(pheader, ents, user_f); - user_l = read_int_with_suffix(cxt, - user_f, /* minimal */ - dflt_l, /* default */ - dflt_l, /* maximal */ - user_f, /* base for relative input */ - _("Last sector, +sectors or +size{K,M,G}"), - &is_suffix_used); - - if (is_suffix_used) - user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1; + fdisk_ask_set_query(ask, _("Last sector, +sectors or +size{K,M,G,T,P}")); + fdisk_ask_set_type(ask, FDISK_ASKTYPE_OFFSET); + fdisk_ask_number_set_low(ask, user_f); /* minimal */ + fdisk_ask_number_set_default(ask, dflt_l); /* default */ + fdisk_ask_number_set_high(ask, dflt_l); /* maximal */ + fdisk_ask_number_set_base(ask, user_f); /* base for relative input */ + fdisk_ask_number_set_unit(ask, cxt->sector_size); + + rc = fdisk_do_ask(cxt, ask); + if (rc) + goto done; + + user_l = fdisk_ask_number_get_result(ask); + if (fdisk_ask_number_is_relative(ask)) + user_l = fdisk_align_lba_in_range(cxt, user_l, user_f, dflt_l) - 1; if (user_l > user_f && user_l <= disk_l) break; } @@ -1650,7 +1654,10 @@ static int gpt_add_partition( fdisk_label_set_changed(cxt->label, 1); } - return 0; + rc = 0; +done: + fdisk_free_ask(ask); + return rc; } /* diff --git a/libfdisk/src/libfdisk.h b/libfdisk/src/libfdisk.h index a19849dc4d..8cf5d65e1c 100644 --- a/libfdisk/src/libfdisk.h +++ b/libfdisk/src/libfdisk.h @@ -49,6 +49,12 @@ enum { FDISK_PARTSTAT_USED /* partition used */ }; +enum { + FDISK_ASKTYPE_NONE = 0, + FDISK_ASKTYPE_NUMBER, + FDISK_ASKTYPE_OFFSET, +}; + /* init.c */ extern void fdisk_init_debug(int mask); @@ -111,13 +117,31 @@ extern int fdisk_dos_enable_compatible(struct fdisk_label *lb, int enable); extern int fdisk_dos_is_compatible(struct fdisk_label *lb); /* ask.c */ -extern const char *fdisk_ask_get_question(struct fdisk_ask *ask); +extern struct fdisk_ask *fdisk_new_ask(void); +extern void fdisk_reset_ask(struct fdisk_ask *ask); +extern void fdisk_free_ask(struct fdisk_ask *ask); +extern const char *fdisk_ask_get_query(struct fdisk_ask *ask); +extern int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str); extern int fdisk_ask_get_type(struct fdisk_ask *ask); +extern int fdisk_ask_set_type(struct fdisk_ask *ask, int type); +extern int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask); + extern const char *fdisk_ask_number_get_range(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range); extern uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt); extern uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low); extern uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high); +extern uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base); +extern uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask); +extern int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit); +extern uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask); extern int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result); +extern int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative); +extern int fdisk_ask_number_is_relative(struct fdisk_ask *ask); #ifdef __cplusplus }