| |
| #include "strutils.h" |
| #include "fdiskP.h" |
| |
| /** |
| * SECTION: ask |
| * @title: Ask |
| * @short_description: interface for dialog driven partitioning, warning and info messages |
| * |
| */ |
| |
| static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask); |
| |
| |
| /** |
| * fdisk_set_ask: |
| * @cxt: context |
| * @ask_cb: callback |
| * @data: callback data |
| * |
| * Set callback for dialog driven partitioning and library warnings/errors. |
| * |
| * Returns: 0 on success, < 0 on error. |
| */ |
| int fdisk_set_ask(struct fdisk_context *cxt, |
| int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *), |
| void *data) |
| { |
| assert(cxt); |
| |
| cxt->ask_cb = ask_cb; |
| cxt->ask_data = data; |
| return 0; |
| } |
| |
| struct fdisk_ask *fdisk_new_ask(void) |
| { |
| struct fdisk_ask *ask = calloc(1, sizeof(struct fdisk_ask)); |
| DBG(ASK, ul_debugobj(ask, "alloc")); |
| ask->refcount = 1; |
| return ask; |
| } |
| |
| void fdisk_reset_ask(struct fdisk_ask *ask) |
| { |
| int refcount; |
| |
| assert(ask); |
| free(ask->query); |
| |
| DBG(ASK, ul_debugobj(ask, "reset")); |
| refcount = ask->refcount; |
| |
| if (fdisk_is_ask(ask, MENU)) |
| fdisk_ask_menu_reset_items(ask); |
| |
| memset(ask, 0, sizeof(*ask)); |
| ask->refcount = refcount; |
| } |
| |
| /** |
| * fdisk_ref_ask: |
| * @ask: ask instance |
| * |
| * Incremparts reference counter. |
| */ |
| void fdisk_ref_ask(struct fdisk_ask *ask) |
| { |
| if (ask) |
| ask->refcount++; |
| } |
| |
| |
| /** |
| * fdisk_unref_ask: |
| * @ask: ask instance |
| * |
| * De-incremparts reference counter, on zero the @ask is automatically |
| * deallocated. |
| */ |
| void fdisk_unref_ask(struct fdisk_ask *ask) |
| { |
| if (!ask) |
| return; |
| ask->refcount--; |
| |
| if (ask->refcount <= 0) { |
| fdisk_reset_ask(ask); |
| DBG(ASK, ul_debugobj(ask, "free")); |
| free(ask); |
| } |
| } |
| |
| /** |
| * fdisk_ask_get_query: |
| * @ask: ask instance |
| * |
| * Returns: pointer to dialog string. |
| */ |
| 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; |
| } |
| |
| /** |
| * fdisk_ask_get_type: |
| * @ask: ask instance |
| * |
| * Returns: FDISK_ASKTYPE_* |
| */ |
| 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, ul_debugobj(ask, "do_ask for '%s'", |
| ask->query ? ask->query : |
| ask->type == FDISK_ASKTYPE_INFO ? "info" : |
| ask->type == FDISK_ASKTYPE_WARNX ? "warnx" : |
| ask->type == FDISK_ASKTYPE_WARN ? "warn" : |
| "?nothing?")); |
| |
| if (!cxt->ask_cb) { |
| DBG(ASK, ul_debugobj(ask, "no ask callback specified!")); |
| return -EINVAL; |
| } |
| |
| rc = cxt->ask_cb(cxt, ask, cxt->ask_data); |
| |
| DBG(ASK, ul_debugobj(ask, "do_ask done [rc=%d]", rc)); |
| return rc; |
| } |
| |
| #define is_number_ask(a) (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET)) |
| |
| /** |
| * fdisk_ask_number_get_range: |
| * @ask: ask instance |
| * |
| * Returns: string with range (e.g. "1,3,5-10") |
| */ |
| const char *fdisk_ask_number_get_range(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(ask)); |
| return ask->data.num.range; |
| } |
| |
| int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range) |
| { |
| assert(ask); |
| assert(is_number_ask(ask)); |
| ask->data.num.range = range; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_number_get_default: |
| * @ask: ask instance |
| * |
| * Returns: default number |
| * |
| */ |
| uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(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; |
| } |
| |
| /** |
| * fdisk_ask_number_get_low: |
| * @ask: ask instance |
| * |
| * Returns: minimal possible number when ask for numbers in range |
| */ |
| uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(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; |
| } |
| |
| /** |
| * fdisk_ask_number_get_high: |
| * @ask: ask instance |
| * |
| * Returns: maximal possible number when ask for numbers in range |
| */ |
| uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(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; |
| } |
| |
| /** |
| * fdisk_ask_number_get_result: |
| * @ask: ask instance |
| * |
| * Returns: result |
| */ |
| uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(ask)); |
| return ask->data.num.result; |
| } |
| |
| /** |
| * fdisk_ask_number_set_result: |
| * @ask: ask instance |
| * @result: dialog result |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result) |
| { |
| assert(ask); |
| ask->data.num.result = result; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_number_get_base: |
| * @ask: ask instance |
| * |
| * Returns: base when user specify number in relative notation (+size) |
| */ |
| uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(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; |
| } |
| |
| /** |
| * fdisk_ask_number_get_unit: |
| * @ask: ask instance |
| * |
| * Returns: number of bytes per the unit |
| */ |
| uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(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); |
| assert(is_number_ask(ask)); |
| return ask->data.num.relative; |
| } |
| |
| /** |
| * fdisk_ask_number_set_relative |
| * @ask: ask instance |
| * @relative: 0 or 1 |
| * |
| * Inform libfdisk that user specified number in relative notation rather than |
| * by explicit number. This info allows to fdisk do some optimization (e.g. |
| * align end of partiton, etc.) |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative) |
| { |
| assert(ask); |
| ask->data.num.relative = relative ? 1 : 0; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_number_inchars: |
| * @ask: ask instance |
| * |
| * For example for BSD is normal to address partition by chars rather than by |
| * number (first partition is 'a'). |
| * |
| * Returns: 1 if number should be presented as chars |
| * |
| */ |
| int fdisk_ask_number_inchars(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_number_ask(ask)); |
| return ask->data.num.inchars; |
| } |
| |
| /* |
| * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur' |
| */ |
| #define tochar(num) ((int) ('a' + num - 1)) |
| static char *mk_string_list(char *ptr, size_t *len, size_t *begin, |
| size_t *run, ssize_t cur, int inchar) |
| { |
| int rlen; |
| |
| if (cur != -1) { |
| if (!*begin) { /* begin of the list */ |
| *begin = cur + 1; |
| return ptr; |
| } |
| |
| if (*begin + *run == cur) { /* no gap, continue */ |
| (*run)++; |
| return ptr; |
| } |
| } else if (!*begin) { |
| *ptr = '\0'; |
| return ptr; /* end of empty list */ |
| } |
| |
| /* add to the list */ |
| if (!*run) |
| rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) : |
| snprintf(ptr, *len, "%zu,", *begin); |
| else if (*run == 1) |
| rlen = inchar ? |
| snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) : |
| snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1); |
| else |
| rlen = inchar ? |
| snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) : |
| snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run); |
| |
| if (rlen < 0 || (size_t) rlen + 1 > *len) |
| return NULL; |
| |
| ptr += rlen; |
| |
| if (rlen > 0 && *len > (size_t) rlen) |
| *len -= rlen; |
| else |
| *len = 0; |
| |
| if (cur == -1 && *begin) { |
| /* end of the list */ |
| *(ptr - 1) = '\0'; /* remove tailing ',' from the list */ |
| return ptr; |
| } |
| |
| *begin = cur + 1; |
| *run = 0; |
| |
| return ptr; |
| } |
| |
| /** |
| * fdisk_ask_partnum: |
| * @cxt: context |
| * @partnum: returns partition number |
| * @wantnew: 0|1 |
| * |
| * High-level API to ask for used or unused partition number. |
| * |
| * Returns: 0 on success, < 0 on error, 1 if no free/used partition |
| */ |
| int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew) |
| { |
| int rc = 0, inchar = 0; |
| char range[BUFSIZ], *ptr = range; |
| size_t i, len = sizeof(range), begin = 0, run = 0; |
| struct fdisk_ask *ask = NULL; |
| __typeof__(ask->data.num) *num; |
| |
| assert(cxt); |
| assert(cxt->label); |
| assert(partnum); |
| |
| if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO) |
| inchar = 1; |
| |
| DBG(ASK, ul_debug("%s: asking for %s partition number " |
| "(max: %zu, inchar: %s)", |
| cxt->label->name, |
| wantnew ? "new" : "used", |
| cxt->label->nparts_max, |
| inchar ? "yes" : "not")); |
| |
| ask = fdisk_new_ask(); |
| if (!ask) |
| return -ENOMEM; |
| |
| fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); |
| num = &ask->data.num; |
| |
| ask->data.num.inchars = inchar ? 1 : 0; |
| |
| for (i = 0; i < cxt->label->nparts_max; i++) { |
| int used = fdisk_is_partition_used(cxt, i); |
| |
| if (wantnew && !used) { |
| ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar); |
| if (!ptr) { |
| rc = -EINVAL; |
| break; |
| } |
| if (!num->low) |
| num->dfl = num->low = i + 1; |
| num->hig = i + 1; |
| } else if (!wantnew && used) { |
| ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar); |
| if (!num->low) |
| num->low = i + 1; |
| num->dfl = num->hig = i + 1; |
| } |
| } |
| |
| DBG(ASK, ul_debugobj(ask, "ask limits: low: %ju, high: %ju, default: %ju", |
| num->low, num->hig, num->dfl)); |
| |
| if (!rc && !wantnew && num->low == num->hig) { |
| if (num->low > 0) { |
| /* only one existing partiton, don't ask, return the number */ |
| fdisk_ask_number_set_result(ask, num->low); |
| fdisk_info(cxt, _("Selected partition %ju"), num->low); |
| |
| } else if (num->low == 0) { |
| fdisk_warnx(cxt, _("No partition is defined yet!")); |
| rc = 1; |
| } |
| goto dont_ask; |
| } |
| if (!rc && wantnew && num->low == num->hig) { |
| if (num->low > 0) { |
| /* only one free partition, don't ask, return the number */ |
| fdisk_ask_number_set_result(ask, num->low); |
| fdisk_info(cxt, _("Selected partition %ju"), num->low); |
| } |
| if (num->low == 0) { |
| fdisk_warnx(cxt, _("No free partition available!")); |
| rc = 1; |
| } |
| goto dont_ask; |
| } |
| if (!rc) { |
| mk_string_list(ptr, &len, &begin, &run, -1, inchar); /* terminate the list */ |
| rc = fdisk_ask_number_set_range(ask, range); |
| } |
| if (!rc) |
| rc = fdisk_ask_set_query(ask, _("Partition number")); |
| if (!rc) |
| rc = fdisk_do_ask(cxt, ask); |
| |
| dont_ask: |
| if (!rc) { |
| *partnum = fdisk_ask_number_get_result(ask); |
| if (*partnum) |
| *partnum -= 1; |
| } |
| DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask), rc)); |
| fdisk_unref_ask(ask); |
| return rc; |
| } |
| |
| /** |
| * fdisk_ask_number: |
| * @cxt: context |
| * @low: minimal possible number |
| * @dflt: default suggestion |
| * @high: maximal possible number |
| * @query: question string |
| * @result: returns result |
| * |
| * Returns: 0 on success, <0 on error. |
| */ |
| int fdisk_ask_number(struct fdisk_context *cxt, |
| uintmax_t low, |
| uintmax_t dflt, |
| uintmax_t high, |
| const char *query, |
| uintmax_t *result) |
| { |
| struct fdisk_ask *ask; |
| int rc; |
| |
| assert(cxt); |
| |
| ask = fdisk_new_ask(); |
| if (!ask) |
| return -ENOMEM; |
| |
| rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER); |
| if (!rc) |
| fdisk_ask_number_set_low(ask, low); |
| if (!rc) |
| fdisk_ask_number_set_default(ask, dflt); |
| if (!rc) |
| fdisk_ask_number_set_high(ask, high); |
| if (!rc) |
| fdisk_ask_set_query(ask, query); |
| if (!rc) |
| rc = fdisk_do_ask(cxt, ask); |
| if (!rc) |
| *result = fdisk_ask_number_get_result(ask); |
| |
| DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", *result, rc)); |
| fdisk_unref_ask(ask); |
| return rc; |
| } |
| |
| /** |
| * fdisk_ask_string_get_result: |
| * @ask: ask instance |
| * |
| * Returns: pointer to dialog result |
| */ |
| char *fdisk_ask_string_get_result(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, STRING)); |
| return ask->data.str.result; |
| } |
| |
| /** |
| * fdisk_ask_string_set_result: |
| * @ask: ask instance |
| * @result: pointer to allocated buffer with string |
| * |
| * You don't have to care about the @result deallocation, libfdisk is going to |
| * deallocate the result when destroy @ask instance. |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result) |
| { |
| assert(ask); |
| ask->data.str.result = result; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_string: |
| * @cxt: context: |
| * @query: question string |
| * @result: returns allocated buffer |
| * |
| * High-level API to ask for strings. Don't forget to deallocate the @result. |
| * |
| * Returns: 0 on success, <0 on error. |
| */ |
| int fdisk_ask_string(struct fdisk_context *cxt, |
| const char *query, |
| char **result) |
| { |
| struct fdisk_ask *ask; |
| int rc; |
| |
| assert(cxt); |
| |
| ask = fdisk_new_ask(); |
| if (!ask) |
| return -ENOMEM; |
| |
| rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING); |
| if (!rc) |
| fdisk_ask_set_query(ask, query); |
| if (!rc) |
| rc = fdisk_do_ask(cxt, ask); |
| if (!rc) |
| *result = fdisk_ask_string_get_result(ask); |
| |
| DBG(ASK, ul_debugobj(ask, "result: %s [rc=%d]\n", *result, rc)); |
| fdisk_unref_ask(ask); |
| return rc; |
| } |
| |
| /** |
| * fdisk_ask_yesno: |
| * @cxt: context |
| * @query: question string |
| * @result: returns 0 (no) or 1 (yes) |
| * |
| * Hight-level API to ask Yes/No questions |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_yesno(struct fdisk_context *cxt, |
| const char *query, |
| int *result) |
| { |
| struct fdisk_ask *ask; |
| int rc; |
| |
| assert(cxt); |
| |
| ask = fdisk_new_ask(); |
| if (!ask) |
| return -ENOMEM; |
| |
| rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO); |
| if (!rc) |
| fdisk_ask_set_query(ask, query); |
| if (!rc) |
| rc = fdisk_do_ask(cxt, ask); |
| if (!rc) |
| *result = fdisk_ask_yesno_get_result(ask) == 1 ? 1 : 0; |
| |
| DBG(ASK, ul_debugobj(ask, "result: %d [rc=%d]\n", *result, rc)); |
| fdisk_unref_ask(ask); |
| return rc; |
| } |
| |
| /** |
| * fdisk_ask_yesno_get_result: |
| * @ask: ask instance |
| * |
| * Returns: 0 or 1 |
| */ |
| int fdisk_ask_yesno_get_result(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, YESNO)); |
| return ask->data.yesno.result; |
| } |
| |
| /** |
| * fdisk_ask_yesno_set_result: |
| * @ask: ask instance |
| * @result: 1 or 0 |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result) |
| { |
| assert(ask); |
| ask->data.yesno.result = result; |
| return 0; |
| } |
| |
| /* |
| * menu |
| */ |
| int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| ask->data.menu.dfl = dfl; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_menu_get_default: |
| * @ask: ask instance |
| * |
| * Returns: default menu item key |
| */ |
| int fdisk_ask_menu_get_default(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| return ask->data.menu.dfl; |
| } |
| |
| /** |
| * fdisk_ask_menu_set_result: |
| * @ask: ask instance |
| * @key: result |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| ask->data.menu.result = key; |
| DBG(ASK, ul_debugobj(ask, "menu result: %c\n", key)); |
| return 0; |
| |
| } |
| |
| /** |
| * fdisk_ask_menu_get_result: |
| * @ask: ask instance |
| * @key: returns selected menu item key |
| * |
| * Returns: 0 on success, <0 on error. |
| */ |
| int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key) |
| { |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| if (key) |
| *key = ask->data.menu.result; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_menu_get_item: |
| * @ask: ask menu instance |
| * @idx: wanted menu item index |
| * @key: returns key of the menu item |
| * @name: returns name of the menu item |
| * @desc: returns description of the menu item |
| * |
| * Returns: 0 on success, <0 on error, >0 if idx out-of-range |
| */ |
| int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key, |
| const char **name, const char **desc) |
| { |
| size_t i; |
| struct ask_menuitem *mi; |
| |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| |
| for (i = 0, mi = ask->data.menu.first; mi; mi = mi->next, i++) { |
| if (i == idx) |
| break; |
| } |
| |
| if (!mi) |
| return 1; /* no more items */ |
| if (key) |
| *key = mi->key; |
| if (name) |
| *name = mi->name; |
| if (desc) |
| *desc = mi->desc; |
| return 0; |
| } |
| |
| static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask) |
| { |
| struct ask_menuitem *mi; |
| |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| |
| for (mi = ask->data.menu.first; mi; ) { |
| struct ask_menuitem *next = mi->next; |
| free(mi); |
| mi = next; |
| } |
| } |
| |
| /** |
| * fdisk_ask_menu_get_nitems: |
| * @ask: ask instance |
| * |
| * Returns: number of menu items |
| */ |
| size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask) |
| { |
| struct ask_menuitem *mi; |
| size_t n; |
| |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| |
| for (n = 0, mi = ask->data.menu.first; mi; mi = mi->next, n++); |
| |
| return n; |
| } |
| |
| int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key, |
| const char *name, const char *desc) |
| { |
| struct ask_menuitem *mi; |
| |
| assert(ask); |
| assert(fdisk_is_ask(ask, MENU)); |
| |
| mi = calloc(1, sizeof(*mi)); |
| if (!mi) |
| return -ENOMEM; |
| mi->key = key; |
| mi->name = name; |
| mi->desc = desc; |
| |
| if (!ask->data.menu.first) |
| ask->data.menu.first = mi; |
| else { |
| struct ask_menuitem *last = ask->data.menu.first; |
| |
| while (last->next) |
| last = last->next; |
| last->next = mi; |
| } |
| |
| DBG(ASK, ul_debugobj(ask, "new menu item: %c, \"%s\" (%s)\n", mi->key, mi->name, mi->desc)); |
| return 0; |
| } |
| |
| |
| /* |
| * print-like |
| */ |
| |
| #define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO)) |
| |
| /** |
| * fdisk_ask_print_get_errno: |
| * @ask: ask instance |
| * |
| * Returns: error number for warning/error messages |
| */ |
| int fdisk_ask_print_get_errno(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_print_ask(ask)); |
| return ask->data.print.errnum; |
| } |
| |
| int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum) |
| { |
| assert(ask); |
| ask->data.print.errnum = errnum; |
| return 0; |
| } |
| |
| /** |
| * fdisk_ask_print_get_mesg: |
| * @ask: ask instance |
| * |
| * Returns: pointer to message |
| */ |
| const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask) |
| { |
| assert(ask); |
| assert(is_print_ask(ask)); |
| return ask->data.print.mesg; |
| } |
| |
| /* does not reallocate the message! */ |
| int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg) |
| { |
| assert(ask); |
| ask->data.print.mesg = mesg; |
| return 0; |
| } |
| |
| static int do_vprint(struct fdisk_context *cxt, int errnum, int type, |
| const char *fmt, va_list va) |
| { |
| struct fdisk_ask *ask; |
| int rc; |
| char *mesg; |
| |
| assert(cxt); |
| |
| if (vasprintf(&mesg, fmt, va) < 0) |
| return -ENOMEM; |
| |
| ask = fdisk_new_ask(); |
| if (!ask) { |
| free(mesg); |
| return -ENOMEM; |
| } |
| |
| fdisk_ask_set_type(ask, type); |
| fdisk_ask_print_set_mesg(ask, mesg); |
| if (errnum >= 0) |
| fdisk_ask_print_set_errno(ask, errnum); |
| rc = fdisk_do_ask(cxt, ask); |
| |
| fdisk_unref_ask(ask); |
| free(mesg); |
| return rc; |
| } |
| |
| /** |
| * fdisk_info: |
| * @cxt: context |
| * @fmt: printf-like formatted string |
| * @...: variable parametrs |
| * |
| * High-level API to print info messages, |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...) |
| { |
| int rc; |
| va_list ap; |
| |
| assert(cxt); |
| va_start(ap, fmt); |
| rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap); |
| va_end(ap); |
| return rc; |
| } |
| |
| /** |
| * fdisk_info: |
| * @cxt: context |
| * @fmt: printf-like formatted string |
| * @...: variable parametrs |
| * |
| * High-level API to print warning message (errno expected) |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...) |
| { |
| int rc; |
| va_list ap; |
| |
| assert(cxt); |
| va_start(ap, fmt); |
| rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap); |
| va_end(ap); |
| return rc; |
| } |
| |
| /** |
| * fdisk_warnx: |
| * @cxt: context |
| * @fmt: printf-like formatted string |
| * @...: variable options |
| * |
| * High-level API to print warning message |
| * |
| * Returns: 0 on success, <0 on error |
| */ |
| int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...) |
| { |
| int rc; |
| va_list ap; |
| |
| assert(cxt); |
| va_start(ap, fmt); |
| rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap); |
| va_end(ap); |
| return rc; |
| } |
| |
| int fdisk_info_new_partition( |
| struct fdisk_context *cxt, |
| int num, fdisk_sector_t start, fdisk_sector_t stop, |
| struct fdisk_parttype *t) |
| { |
| int rc; |
| char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE, |
| (uint64_t)(stop - start + 1) * cxt->sector_size); |
| |
| rc = fdisk_info(cxt, |
| _("Created a new partition %d of type '%s' and of size %s."), |
| num, t ? t->name : _("Unknown"), str); |
| free(str); |
| return rc; |
| } |
| |
| #ifdef TEST_PROGRAM |
| int test_ranges(struct fdisk_test *ts, int argc, char *argv[]) |
| { |
| /* 1 - 3, 6, 8, 9, 11 13 */ |
| size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 }; |
| size_t numx[] = { 0, 0, 0 }; |
| char range[BUFSIZ], *ptr = range; |
| size_t i, len = sizeof(range), begin = 0, run = 0; |
| |
| for (i = 0; i < ARRAY_SIZE(nums); i++) { |
| if (!nums[i]) |
| continue; |
| ptr = mk_string_list(ptr, &len, &begin, &run, i, 0); |
| } |
| mk_string_list(ptr, &len, &begin, &run, -1, 0); |
| printf("list: '%s'\n", range); |
| |
| ptr = range; |
| len = sizeof(range), begin = 0, run = 0; |
| for (i = 0; i < ARRAY_SIZE(numx); i++) { |
| if (!numx[i]) |
| continue; |
| ptr = mk_string_list(ptr, &len, &begin, &run, i, 0); |
| } |
| mk_string_list(ptr, &len, &begin, &run, -1, 0); |
| printf("empty list: '%s'\n", range); |
| |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| struct fdisk_test tss[] = { |
| { "--ranges", test_ranges, "generates ranges" }, |
| { NULL } |
| }; |
| |
| return fdisk_run_test(tss, argc, argv); |
| } |
| |
| #endif |