(root)/
util-linux-2.39/
libfdisk/
src/
ask.c
       1  
       2  #include "strutils.h"
       3  #include "fdiskP.h"
       4  
       5  /**
       6   * SECTION: ask
       7   * @title: Ask
       8   * @short_description: interface for dialog driven partitioning, warning and info messages
       9   *
      10   */
      11  
      12  static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask);
      13  
      14  
      15  /**
      16   * fdisk_set_ask:
      17   * @cxt: context
      18   * @ask_cb: callback
      19   * @data: callback data
      20   *
      21   * Set callback for dialog driven partitioning and library warnings/errors.
      22   *
      23   * Returns: 0 on success, < 0 on error.
      24   */
      25  int fdisk_set_ask(struct fdisk_context *cxt,
      26  		int (*ask_cb)(struct fdisk_context *, struct fdisk_ask *, void *),
      27  		void *data)
      28  {
      29  	assert(cxt);
      30  
      31  	cxt->ask_cb = ask_cb;
      32  	cxt->ask_data = data;
      33  	return 0;
      34  }
      35  
      36  struct fdisk_ask *fdisk_new_ask(void)
      37  {
      38  	struct fdisk_ask *ask = calloc(1, sizeof(struct fdisk_ask));
      39  
      40  	if (!ask)
      41  		return NULL;
      42  
      43  	DBG(ASK, ul_debugobj(ask, "alloc"));
      44  	ask->refcount = 1;
      45  	return ask;
      46  }
      47  
      48  void fdisk_reset_ask(struct fdisk_ask *ask)
      49  {
      50  	int refcount;
      51  
      52  	assert(ask);
      53  	free(ask->query);
      54  
      55  	DBG(ASK, ul_debugobj(ask, "reset"));
      56  	refcount = ask->refcount;
      57  
      58  	if (fdisk_is_ask(ask, MENU))
      59  		fdisk_ask_menu_reset_items(ask);
      60  
      61  	memset(ask, 0, sizeof(*ask));
      62  	ask->refcount = refcount;
      63  }
      64  
      65  /**
      66   * fdisk_ref_ask:
      67   * @ask: ask instance
      68   *
      69   * Increments reference counter.
      70   */
      71  void fdisk_ref_ask(struct fdisk_ask *ask)
      72  {
      73  	if (ask)
      74  		ask->refcount++;
      75  }
      76  
      77  
      78  /**
      79   * fdisk_unref_ask:
      80   * @ask: ask instance
      81   *
      82   * Decrements reference counter, on zero the @ask is automatically
      83   * deallocated.
      84   */
      85  void fdisk_unref_ask(struct fdisk_ask *ask)
      86  {
      87  	if (!ask)
      88  		return;
      89  	ask->refcount--;
      90  
      91  	if (ask->refcount <= 0) {
      92  		fdisk_reset_ask(ask);
      93  		DBG(ASK, ul_debugobj(ask, "free"));
      94  		free(ask);
      95  	}
      96  }
      97  
      98  /**
      99   * fdisk_ask_get_query:
     100   * @ask: ask instance
     101   *
     102   * Returns: pointer to dialog string.
     103   */
     104  const char *fdisk_ask_get_query(struct fdisk_ask *ask)
     105  {
     106  	assert(ask);
     107  	return ask->query;
     108  }
     109  
     110  int fdisk_ask_set_query(struct fdisk_ask *ask, const char *str)
     111  {
     112  	assert(ask);
     113  	return strdup_to_struct_member(ask, query, str);
     114  }
     115  
     116  /**
     117   * fdisk_ask_get_type:
     118   * @ask: ask instance
     119   *
     120   * Returns: FDISK_ASKTYPE_*
     121   */
     122  int fdisk_ask_get_type(struct fdisk_ask *ask)
     123  {
     124  	assert(ask);
     125  	return ask->type;
     126  }
     127  
     128  int fdisk_ask_set_type(struct fdisk_ask *ask, int type)
     129  {
     130  	assert(ask);
     131  	ask->type = type;
     132  	return 0;
     133  }
     134  
     135  int fdisk_do_ask(struct fdisk_context *cxt, struct fdisk_ask *ask)
     136  {
     137  	int rc;
     138  
     139  	assert(ask);
     140  	assert(cxt);
     141  
     142  	DBG(ASK, ul_debugobj(ask, "do_ask for '%s'",
     143  				ask->query ? ask->query :
     144  				ask->type == FDISK_ASKTYPE_INFO  ? "info" :
     145  				ask->type == FDISK_ASKTYPE_WARNX ? "warnx" :
     146  				ask->type == FDISK_ASKTYPE_WARN  ? "warn" :
     147  				"?nothing?"));
     148  
     149  	if (!fdisk_has_dialogs(cxt) &&
     150  	    !(ask->type == FDISK_ASKTYPE_INFO ||
     151  	      ask->type == FDISK_ASKTYPE_WARNX ||
     152  	      ask->type == FDISK_ASKTYPE_WARN)) {
     153  		DBG(ASK, ul_debugobj(ask, "dialogs disabled"));
     154  		return -EINVAL;
     155  	}
     156  
     157  	if (!cxt->ask_cb) {
     158  		DBG(ASK, ul_debugobj(ask, "no ask callback specified!"));
     159  		return -EINVAL;
     160  	}
     161  
     162  	rc = cxt->ask_cb(cxt, ask, cxt->ask_data);
     163  
     164  	DBG(ASK, ul_debugobj(ask, "do_ask done [rc=%d]", rc));
     165  	return rc;
     166  }
     167  
     168  #define is_number_ask(a)  (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))
     169  
     170  /**
     171   * fdisk_ask_number_get_range:
     172   * @ask: ask instance
     173   *
     174   * Returns: string with range (e.g. "1,3,5-10")
     175   */
     176  const char *fdisk_ask_number_get_range(struct fdisk_ask *ask)
     177  {
     178  	assert(ask);
     179  	assert(is_number_ask(ask));
     180  	return ask->data.num.range;
     181  }
     182  
     183  int fdisk_ask_number_set_range(struct fdisk_ask *ask, const char *range)
     184  {
     185  	assert(ask);
     186  	assert(is_number_ask(ask));
     187  	ask->data.num.range = range;
     188  	return 0;
     189  }
     190  
     191  /**
     192   * fdisk_ask_number_get_default:
     193   * @ask: ask instance
     194   *
     195   * Returns: default number
     196   *
     197   */
     198  uint64_t fdisk_ask_number_get_default(struct fdisk_ask *ask)
     199  {
     200  	assert(ask);
     201  	assert(is_number_ask(ask));
     202  	return ask->data.num.dfl;
     203  }
     204  
     205  int fdisk_ask_number_set_default(struct fdisk_ask *ask, uint64_t dflt)
     206  {
     207  	assert(ask);
     208  	ask->data.num.dfl = dflt;
     209  	return 0;
     210  }
     211  
     212  /**
     213   * fdisk_ask_number_get_low:
     214   * @ask: ask instance
     215   *
     216   * Returns: minimal possible number when ask for numbers in range
     217   */
     218  uint64_t fdisk_ask_number_get_low(struct fdisk_ask *ask)
     219  {
     220  	assert(ask);
     221  	assert(is_number_ask(ask));
     222  	return ask->data.num.low;
     223  }
     224  
     225  int fdisk_ask_number_set_low(struct fdisk_ask *ask, uint64_t low)
     226  {
     227  	assert(ask);
     228  	ask->data.num.low = low;
     229  	return 0;
     230  }
     231  
     232  /**
     233   * fdisk_ask_number_get_high:
     234   * @ask: ask instance
     235   *
     236   * Returns: maximal possible number when ask for numbers in range
     237   */
     238  uint64_t fdisk_ask_number_get_high(struct fdisk_ask *ask)
     239  {
     240  	assert(ask);
     241  	assert(is_number_ask(ask));
     242  	return ask->data.num.hig;
     243  }
     244  
     245  int fdisk_ask_number_set_high(struct fdisk_ask *ask, uint64_t high)
     246  {
     247  	assert(ask);
     248  	ask->data.num.hig = high;
     249  	return 0;
     250  }
     251  
     252  /**
     253   * fdisk_ask_number_get_result:
     254   * @ask: ask instance
     255   *
     256   * Returns: result
     257   */
     258  uint64_t fdisk_ask_number_get_result(struct fdisk_ask *ask)
     259  {
     260  	assert(ask);
     261  	assert(is_number_ask(ask));
     262  	return ask->data.num.result;
     263  }
     264  
     265  /**
     266   * fdisk_ask_number_set_result:
     267   * @ask: ask instance
     268   * @result: dialog result
     269   *
     270   * Returns: 0 on success, <0 on error
     271   */
     272  int fdisk_ask_number_set_result(struct fdisk_ask *ask, uint64_t result)
     273  {
     274  	assert(ask);
     275  	ask->data.num.result = result;
     276  	return 0;
     277  }
     278  
     279  /**
     280   * fdisk_ask_number_get_base:
     281   * @ask: ask instance
     282   *
     283   * Returns: base when user specify number in relative notation (+size)
     284   */
     285  uint64_t fdisk_ask_number_get_base(struct fdisk_ask *ask)
     286  {
     287  	assert(ask);
     288  	assert(is_number_ask(ask));
     289  	return ask->data.num.base;
     290  }
     291  
     292  int fdisk_ask_number_set_base(struct fdisk_ask *ask, uint64_t base)
     293  {
     294  	assert(ask);
     295  	ask->data.num.base = base;
     296  	return 0;
     297  }
     298  
     299  /**
     300   * fdisk_ask_number_get_unit:
     301   * @ask: ask instance
     302   *
     303   * Returns: number of bytes per the unit
     304   */
     305  uint64_t fdisk_ask_number_get_unit(struct fdisk_ask *ask)
     306  {
     307  	assert(ask);
     308  	assert(is_number_ask(ask));
     309  	return ask->data.num.unit;
     310  }
     311  
     312  int fdisk_ask_number_set_unit(struct fdisk_ask *ask, uint64_t unit)
     313  {
     314  	assert(ask);
     315  	ask->data.num.unit = unit;
     316  	return 0;
     317  }
     318  
     319  int fdisk_ask_number_is_relative(struct fdisk_ask *ask)
     320  {
     321  	assert(ask);
     322  	assert(is_number_ask(ask));
     323  	return ask->data.num.relative;
     324  }
     325  
     326  /**
     327   * fdisk_ask_number_is_wrap_negative:
     328   * @ask: ask instance
     329   *
     330   * The wrap-negative flag can be used to accept negative number from user. In this
     331   * case the dialog result is calculated as "high - num" (-N from high limit).
     332   *
     333   * Returns: 1 or 0.
     334   *
     335   * Since: 2.33
     336   */
     337  int fdisk_ask_number_is_wrap_negative(struct fdisk_ask *ask)
     338  {
     339  	assert(ask);
     340  	assert(is_number_ask(ask));
     341  	return ask->data.num.wrap_negative;
     342  }
     343  
     344  /**
     345   * fdisk_ask_number_set_relative
     346   * @ask: ask instance
     347   * @relative: 0 or 1
     348   *
     349   * Inform libfdisk that user can specify the number in relative notation rather than
     350   * by explicit number. This is useful for some optimization (e.g.
     351   * align end of partition, etc.)
     352   *
     353   * Returns: 0 on success, <0 on error
     354   */
     355  int fdisk_ask_number_set_relative(struct fdisk_ask *ask, int relative)
     356  {
     357  	assert(ask);
     358  	ask->data.num.relative = relative ? 1 : 0;
     359  	return 0;
     360  }
     361  
     362  /**
     363   * fdisk_ask_number_inchars:
     364   * @ask: ask instance
     365   *
     366   * For example for BSD is normal to address partition by chars rather than by
     367   * number (first partition is 'a').
     368   *
     369   * Returns: 1 if number should be presented as chars
     370   *
     371   */
     372  int fdisk_ask_number_inchars(struct fdisk_ask *ask)
     373  {
     374  	assert(ask);
     375  	assert(is_number_ask(ask));
     376  	return ask->data.num.inchars;
     377  }
     378  
     379  int fdisk_ask_number_set_wrap_negative(struct fdisk_ask *ask, int wrap_negative)
     380  {
     381  	assert(ask);
     382  	ask->data.num.wrap_negative = wrap_negative ? 1 : 0;
     383  	return 0;
     384  }
     385  
     386  /*
     387   * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
     388   */
     389  #define tochar(num)	((int) ('a' + num - 1))
     390  static char *mk_string_list(char *ptr, size_t *len, size_t *begin,
     391  			    size_t *run, ssize_t cur, int inchar)
     392  {
     393  	int rlen;
     394  
     395  	if (cur != -1) {
     396  		if (!*begin) {			/* begin of the list */
     397  			*begin = cur + 1;
     398  			return ptr;
     399  		}
     400  
     401  		if (*begin + *run == (size_t)cur) {	/* no gap, continue */
     402  			(*run)++;
     403  			return ptr;
     404  		}
     405  	} else if (!*begin) {
     406  		*ptr = '\0';
     407  		return ptr;		/* end of empty list */
     408  	}
     409  
     410  					/* add to the list */
     411  	if (!*run)
     412  		rlen = inchar ? snprintf(ptr, *len, "%c,", tochar(*begin)) :
     413  				snprintf(ptr, *len, "%zu,", *begin);
     414  	else if (*run == 1)
     415  		rlen = inchar ?
     416  			snprintf(ptr, *len, "%c,%c,", tochar(*begin), tochar(*begin + 1)) :
     417  			snprintf(ptr, *len, "%zu,%zu,", *begin, *begin + 1);
     418  	else
     419  		rlen = inchar ?
     420  			snprintf(ptr, *len, "%c-%c,", tochar(*begin), tochar(*begin + *run)) :
     421  			snprintf(ptr, *len, "%zu-%zu,", *begin, *begin + *run);
     422  
     423  	if (rlen < 0 || (size_t) rlen >= *len)
     424  		return NULL;
     425  
     426  	ptr += rlen;
     427  	*len -= rlen;
     428  
     429  	if (cur == -1 && *begin) {
     430  		/* end of the list */
     431  		*(ptr - 1) = '\0';	/* remove tailing ',' from the list */
     432  		return ptr;
     433  	}
     434  
     435  	*begin = cur + 1;
     436  	*run = 0;
     437  
     438  	return ptr;
     439  }
     440  
     441  /**
     442   * fdisk_ask_partnum:
     443   * @cxt: context
     444   * @partnum: returns partition number
     445   * @wantnew: 0|1
     446   *
     447   * High-level API to ask for used or unused partition number.
     448   *
     449   * Returns: 0 on success, < 0 on error, 1 if no free/used partition
     450   */
     451  int fdisk_ask_partnum(struct fdisk_context *cxt, size_t *partnum, int wantnew)
     452  {
     453  	int rc = 0, inchar = 0;
     454  	char range[BUFSIZ], *ptr = range;
     455  	size_t i, len = sizeof(range), begin = 0, run = 0;
     456  	struct fdisk_ask *ask = NULL;
     457  	__typeof__(ask->data.num) *num;
     458  
     459  	assert(cxt);
     460  	assert(cxt->label);
     461  	assert(partnum);
     462  
     463  	if (cxt->label && cxt->label->flags & FDISK_LABEL_FL_INCHARS_PARTNO)
     464  		inchar = 1;
     465  
     466  	DBG(ASK, ul_debug("%s: asking for %s partition number "
     467  			  "(max: %zu, inchar: %s)",
     468  			cxt->label ? cxt->label->name : "???",
     469  			wantnew ? "new" : "used",
     470  			cxt->label ? cxt->label->nparts_max : 0,
     471  			inchar ? "yes" : "not"));
     472  
     473  	ask = fdisk_new_ask();
     474  	if (!ask)
     475  		return -ENOMEM;
     476  
     477  	fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
     478  	num = &ask->data.num;
     479  
     480  	ask->data.num.inchars = inchar ? 1 : 0;
     481  
     482  	for (i = 0; i < cxt->label->nparts_max; i++) {
     483  		int used = fdisk_is_partition_used(cxt, i);
     484  
     485  		if (wantnew && !used) {
     486  			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
     487  			if (!ptr) {
     488  				rc = -EINVAL;
     489  				break;
     490  			}
     491  			if (!num->low)
     492  				num->dfl = num->low = i + 1;
     493  			num->hig = i + 1;
     494  		} else if (!wantnew && used) {
     495  			ptr = mk_string_list(ptr, &len, &begin, &run, i, inchar);
     496  			if (!num->low)
     497  				num->low = i + 1;
     498  			num->dfl = num->hig = i + 1;
     499  		}
     500  	}
     501  
     502  	DBG(ASK, ul_debugobj(ask, "ask limits: low: %"PRIu64", high: %"PRIu64", default: %"PRIu64"",
     503  				num->low, num->hig, num->dfl));
     504  
     505  	if (!rc && !wantnew && num->low == num->hig) {
     506  		if (num->low > 0) {
     507  			/* only one existing partition, don't ask, return the number */
     508  			fdisk_ask_number_set_result(ask, num->low);
     509  			fdisk_info(cxt, _("Selected partition %ju"), num->low);
     510  
     511  		} else if (num->low == 0) {
     512  			fdisk_warnx(cxt, _("No partition is defined yet!"));
     513  			rc = 1;
     514  		}
     515  		goto dont_ask;
     516  	}
     517  	if (!rc && wantnew && num->low == num->hig) {
     518  		if (num->low > 0) {
     519  			/* only one free partition, don't ask, return the number */
     520  			fdisk_ask_number_set_result(ask, num->low);
     521  			fdisk_info(cxt, _("Selected partition %ju"), num->low);
     522  		}
     523  		if (num->low == 0) {
     524  			fdisk_warnx(cxt, _("No free partition available!"));
     525  			rc = 1;
     526  		}
     527  		goto dont_ask;
     528  	}
     529  	if (!rc) {
     530  		mk_string_list(ptr, &len, &begin, &run, -1, inchar);	/* terminate the list */
     531  		rc = fdisk_ask_number_set_range(ask, range);
     532  	}
     533  	if (!rc)
     534  		rc = fdisk_ask_set_query(ask, _("Partition number"));
     535  	if (!rc)
     536  		rc = fdisk_do_ask(cxt, ask);
     537  
     538  dont_ask:
     539  	if (!rc) {
     540  		*partnum = fdisk_ask_number_get_result(ask);
     541  		if (*partnum)
     542  			*partnum -= 1;
     543  	}
     544  	DBG(ASK, ul_debugobj(ask, "result: %"PRIu64" [rc=%d]\n", fdisk_ask_number_get_result(ask), rc));
     545  	fdisk_unref_ask(ask);
     546  	return rc;
     547  }
     548  
     549  /**
     550   * fdisk_ask_number:
     551   * @cxt: context
     552   * @low: minimal possible number
     553   * @dflt: default suggestion
     554   * @high: maximal possible number
     555   * @query: question string
     556   * @result: returns result
     557   *
     558   * Returns: 0 on success, <0 on error.
     559   */
     560  int fdisk_ask_number(struct fdisk_context *cxt,
     561  		     uintmax_t low,
     562  		     uintmax_t dflt,
     563  		     uintmax_t high,
     564  		     const char *query,
     565  		     uintmax_t *result)
     566  {
     567  	struct fdisk_ask *ask;
     568  	int rc;
     569  
     570  	assert(cxt);
     571  
     572  	ask = fdisk_new_ask();
     573  	if (!ask)
     574  		return -ENOMEM;
     575  
     576  	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_NUMBER);
     577  	if (!rc)
     578  		fdisk_ask_number_set_low(ask, low);
     579  	if (!rc)
     580  		fdisk_ask_number_set_default(ask, dflt);
     581  	if (!rc)
     582  		fdisk_ask_number_set_high(ask, high);
     583  	if (!rc)
     584  		fdisk_ask_set_query(ask, query);
     585  	if (!rc)
     586  		rc = fdisk_do_ask(cxt, ask);
     587  	if (!rc)
     588  		*result = fdisk_ask_number_get_result(ask);
     589  
     590  	DBG(ASK, ul_debugobj(ask, "result: %ju [rc=%d]\n", *result, rc));
     591  	fdisk_unref_ask(ask);
     592  	return rc;
     593  }
     594  
     595  /**
     596   * fdisk_ask_string_get_result:
     597   * @ask: ask instance
     598   *
     599   * Returns: pointer to dialog result
     600   */
     601  char *fdisk_ask_string_get_result(struct fdisk_ask *ask)
     602  {
     603  	assert(ask);
     604  	assert(fdisk_is_ask(ask, STRING));
     605  	return ask->data.str.result;
     606  }
     607  
     608  /**
     609   * fdisk_ask_string_set_result:
     610   * @ask: ask instance
     611   * @result: pointer to allocated buffer with string
     612   *
     613   * You don't have to care about the @result deallocation, libfdisk is going to
     614   * deallocate the result when destroy @ask instance.
     615   *
     616   * Returns: 0 on success, <0 on error
     617   */
     618  int fdisk_ask_string_set_result(struct fdisk_ask *ask, char *result)
     619  {
     620  	assert(ask);
     621  	ask->data.str.result = result;
     622  	return 0;
     623  }
     624  
     625  /**
     626   * fdisk_ask_string:
     627   * @cxt: context:
     628   * @query: question string
     629   * @result: returns allocated buffer
     630   *
     631   * High-level API to ask for strings. Don't forget to deallocate the @result.
     632   *
     633   * Returns: 0 on success, <0 on error.
     634   */
     635  int fdisk_ask_string(struct fdisk_context *cxt,
     636  		     const char *query,
     637  		     char **result)
     638  {
     639  	struct fdisk_ask *ask;
     640  	int rc;
     641  
     642  	assert(cxt);
     643  
     644  	ask = fdisk_new_ask();
     645  	if (!ask)
     646  		return -ENOMEM;
     647  
     648  	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_STRING);
     649  	if (!rc)
     650  		fdisk_ask_set_query(ask, query);
     651  	if (!rc)
     652  		rc = fdisk_do_ask(cxt, ask);
     653  	if (!rc)
     654  		*result = fdisk_ask_string_get_result(ask);
     655  
     656  	DBG(ASK, ul_debugobj(ask, "result: %s [rc=%d]\n", *result, rc));
     657  	fdisk_unref_ask(ask);
     658  	return rc;
     659  }
     660  
     661  /**
     662   * fdisk_ask_yesno:
     663   * @cxt: context
     664   * @query: question string
     665   * @result: returns 0 (no) or 1 (yes)
     666   *
     667   * High-level API to ask Yes/No questions
     668   *
     669   * Returns: 0 on success, <0 on error
     670   */
     671  int fdisk_ask_yesno(struct fdisk_context *cxt,
     672  		     const char *query,
     673  		     int *result)
     674  {
     675  	struct fdisk_ask *ask;
     676  	int rc;
     677  
     678  	assert(cxt);
     679  
     680  	ask = fdisk_new_ask();
     681  	if (!ask)
     682  		return -ENOMEM;
     683  
     684  	rc = fdisk_ask_set_type(ask, FDISK_ASKTYPE_YESNO);
     685  	if (!rc)
     686  		fdisk_ask_set_query(ask, query);
     687  	if (!rc)
     688  		rc = fdisk_do_ask(cxt, ask);
     689  	if (!rc)
     690  		*result = fdisk_ask_yesno_get_result(ask) == 1 ? 1 : 0;
     691  
     692  	DBG(ASK, ul_debugobj(ask, "result: %d [rc=%d]\n", *result, rc));
     693  	fdisk_unref_ask(ask);
     694  	return rc;
     695  }
     696  
     697  /**
     698   * fdisk_ask_yesno_get_result:
     699   * @ask: ask instance
     700   *
     701   * Returns: 0 or 1
     702   */
     703  int fdisk_ask_yesno_get_result(struct fdisk_ask *ask)
     704  {
     705  	assert(ask);
     706  	assert(fdisk_is_ask(ask, YESNO));
     707  	return ask->data.yesno.result;
     708  }
     709  
     710  /**
     711   * fdisk_ask_yesno_set_result:
     712   * @ask: ask instance
     713   * @result: 1 or 0
     714   *
     715   * Returns: 0 on success, <0 on error
     716   */
     717  int fdisk_ask_yesno_set_result(struct fdisk_ask *ask, int result)
     718  {
     719  	assert(ask);
     720  	ask->data.yesno.result = result;
     721  	return 0;
     722  }
     723  
     724  /*
     725   * menu
     726   */
     727  int fdisk_ask_menu_set_default(struct fdisk_ask *ask, int dfl)
     728  {
     729  	assert(ask);
     730  	assert(fdisk_is_ask(ask, MENU));
     731  	ask->data.menu.dfl = dfl;
     732  	return 0;
     733  }
     734  
     735  /**
     736   * fdisk_ask_menu_get_default:
     737   * @ask: ask instance
     738   *
     739   * Returns: default menu item key
     740   */
     741  int fdisk_ask_menu_get_default(struct fdisk_ask *ask)
     742  {
     743  	assert(ask);
     744  	assert(fdisk_is_ask(ask, MENU));
     745  	return ask->data.menu.dfl;
     746  }
     747  
     748  /**
     749   * fdisk_ask_menu_set_result:
     750   * @ask: ask instance
     751   * @key: result
     752   *
     753   * Returns: 0 on success, <0 on error
     754   */
     755  int fdisk_ask_menu_set_result(struct fdisk_ask *ask, int key)
     756  {
     757  	assert(ask);
     758  	assert(fdisk_is_ask(ask, MENU));
     759  	ask->data.menu.result = key;
     760  	DBG(ASK, ul_debugobj(ask, "menu result: %c\n", key));
     761  	return 0;
     762  
     763  }
     764  
     765  /**
     766   * fdisk_ask_menu_get_result:
     767   * @ask: ask instance
     768   * @key: returns selected menu item key
     769   *
     770   * Returns: 0 on success, <0 on error.
     771   */
     772  int fdisk_ask_menu_get_result(struct fdisk_ask *ask, int *key)
     773  {
     774  	assert(ask);
     775  	assert(fdisk_is_ask(ask, MENU));
     776  	if (key)
     777  		*key =  ask->data.menu.result;
     778  	return 0;
     779  }
     780  
     781  /**
     782   * fdisk_ask_menu_get_item:
     783   * @ask: ask menu instance
     784   * @idx: wanted menu item index
     785   * @key: returns key of the menu item
     786   * @name: returns name of the menu item
     787   * @desc: returns description of the menu item
     788   *
     789   * Returns: 0 on success, <0 on error, >0 if idx out-of-range
     790   */
     791  int fdisk_ask_menu_get_item(struct fdisk_ask *ask, size_t idx, int *key,
     792  			    const char **name, const char **desc)
     793  {
     794  	size_t i;
     795  	struct ask_menuitem *mi;
     796  
     797  	assert(ask);
     798  	assert(fdisk_is_ask(ask, MENU));
     799  
     800  	for (i = 0, mi = ask->data.menu.first; mi; mi = mi->next, i++) {
     801  		if (i == idx)
     802  			break;
     803  	}
     804  
     805  	if (!mi)
     806  		return 1;	/* no more items */
     807  	if (key)
     808  		*key = mi->key;
     809  	if (name)
     810  		*name = mi->name;
     811  	if (desc)
     812  		*desc = mi->desc;
     813  	return 0;
     814  }
     815  
     816  static void fdisk_ask_menu_reset_items(struct fdisk_ask *ask)
     817  {
     818  	struct ask_menuitem *mi;
     819  
     820  	assert(ask);
     821  	assert(fdisk_is_ask(ask, MENU));
     822  
     823  	for (mi = ask->data.menu.first; mi; ) {
     824  		struct ask_menuitem *next = mi->next;
     825  		free(mi);
     826  		mi = next;
     827  	}
     828  }
     829  
     830  /**
     831   * fdisk_ask_menu_get_nitems:
     832   * @ask: ask instance
     833   *
     834   * Returns: number of menu items
     835   */
     836  size_t fdisk_ask_menu_get_nitems(struct fdisk_ask *ask)
     837  {
     838  	struct ask_menuitem *mi;
     839  	size_t n;
     840  
     841  	assert(ask);
     842  	assert(fdisk_is_ask(ask, MENU));
     843  
     844  	for (n = 0, mi = ask->data.menu.first; mi; mi = mi->next, n++);
     845  
     846  	return n;
     847  }
     848  
     849  int fdisk_ask_menu_add_item(struct fdisk_ask *ask, int key,
     850  			const char *name, const char *desc)
     851  {
     852  	struct ask_menuitem *mi;
     853  
     854  	assert(ask);
     855  	assert(fdisk_is_ask(ask, MENU));
     856  
     857  	mi = calloc(1, sizeof(*mi));
     858  	if (!mi)
     859  		return -ENOMEM;
     860  	mi->key = key;
     861  	mi->name = name;
     862  	mi->desc = desc;
     863  
     864  	if (!ask->data.menu.first)
     865  		ask->data.menu.first = mi;
     866  	else {
     867  	        struct ask_menuitem *last = ask->data.menu.first;
     868  
     869  		while (last->next)
     870  			last = last->next;
     871  		last->next = mi;
     872  	}
     873  
     874  	DBG(ASK, ul_debugobj(ask, "new menu item: %c, \"%s\" (%s)\n", mi->key, mi->name, mi->desc));
     875  	return 0;
     876  }
     877  
     878  
     879  /*
     880   * print-like
     881   */
     882  
     883  #define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))
     884  
     885  /**
     886   * fdisk_ask_print_get_errno:
     887   * @ask: ask instance
     888   *
     889   * Returns: error number for warning/error messages
     890   */
     891  int fdisk_ask_print_get_errno(struct fdisk_ask *ask)
     892  {
     893  	assert(ask);
     894  	assert(is_print_ask(ask));
     895  	return ask->data.print.errnum;
     896  }
     897  
     898  int fdisk_ask_print_set_errno(struct fdisk_ask *ask, int errnum)
     899  {
     900  	assert(ask);
     901  	ask->data.print.errnum = errnum;
     902  	return 0;
     903  }
     904  
     905  /**
     906   * fdisk_ask_print_get_mesg:
     907   * @ask: ask instance
     908   *
     909   * Returns: pointer to message
     910   */
     911  const char *fdisk_ask_print_get_mesg(struct fdisk_ask *ask)
     912  {
     913  	assert(ask);
     914  	assert(is_print_ask(ask));
     915  	return ask->data.print.mesg;
     916  }
     917  
     918  /* does not reallocate the message! */
     919  int fdisk_ask_print_set_mesg(struct fdisk_ask *ask, const char *mesg)
     920  {
     921  	assert(ask);
     922  	ask->data.print.mesg = mesg;
     923  	return 0;
     924  }
     925  
     926  static int do_vprint(struct fdisk_context *cxt, int errnum, int type,
     927  		     const char *fmt, va_list va)
     928  {
     929  	struct fdisk_ask *ask;
     930  	int rc;
     931  	char *mesg;
     932  
     933  	assert(cxt);
     934  
     935  	if (vasprintf(&mesg, fmt, va) < 0)
     936  		return -ENOMEM;
     937  
     938  	ask = fdisk_new_ask();
     939  	if (!ask) {
     940  		free(mesg);
     941  		return -ENOMEM;
     942  	}
     943  
     944  	fdisk_ask_set_type(ask, type);
     945  	fdisk_ask_print_set_mesg(ask, mesg);
     946  	if (errnum >= 0)
     947  		fdisk_ask_print_set_errno(ask, errnum);
     948  	rc = fdisk_do_ask(cxt, ask);
     949  
     950  	fdisk_unref_ask(ask);
     951  	free(mesg);
     952  	return rc;
     953  }
     954  
     955  /**
     956   * fdisk_info:
     957   * @cxt: context
     958   * @fmt: printf-like formatted string
     959   * @...: variable parameters
     960   *
     961   * High-level API to print info messages,
     962   *
     963   * Returns: 0 on success, <0 on error
     964   */
     965  int fdisk_info(struct fdisk_context *cxt, const char *fmt, ...)
     966  {
     967  	int rc;
     968  	va_list ap;
     969  
     970  	assert(cxt);
     971  	va_start(ap, fmt);
     972  	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_INFO, fmt, ap);
     973  	va_end(ap);
     974  	return rc;
     975  }
     976  
     977  /**
     978   * fdisk_info:
     979   * @cxt: context
     980   * @fmt: printf-like formatted string
     981   * @...: variable parameters
     982   *
     983   * High-level API to print warning message (errno expected)
     984   *
     985   * Returns: 0 on success, <0 on error
     986   */
     987  int fdisk_warn(struct fdisk_context *cxt, const char *fmt, ...)
     988  {
     989  	int rc;
     990  	va_list ap;
     991  
     992  	assert(cxt);
     993  	va_start(ap, fmt);
     994  	rc = do_vprint(cxt, errno, FDISK_ASKTYPE_WARN, fmt, ap);
     995  	va_end(ap);
     996  	return rc;
     997  }
     998  
     999  /**
    1000   * fdisk_warnx:
    1001   * @cxt: context
    1002   * @fmt: printf-like formatted string
    1003   * @...: variable options
    1004   *
    1005   * High-level API to print warning message
    1006   *
    1007   * Returns: 0 on success, <0 on error
    1008   */
    1009  int fdisk_warnx(struct fdisk_context *cxt, const char *fmt, ...)
    1010  {
    1011  	int rc;
    1012  	va_list ap;
    1013  
    1014  	assert(cxt);
    1015  	va_start(ap, fmt);
    1016  	rc = do_vprint(cxt, -1, FDISK_ASKTYPE_WARNX, fmt, ap);
    1017  	va_end(ap);
    1018  	return rc;
    1019  }
    1020  
    1021  int fdisk_info_new_partition(
    1022  			struct fdisk_context *cxt,
    1023  			int num, fdisk_sector_t start, fdisk_sector_t stop,
    1024  			struct fdisk_parttype *t)
    1025  {
    1026  	int rc;
    1027  	char *str = size_to_human_string(SIZE_SUFFIX_3LETTER | SIZE_SUFFIX_SPACE,
    1028  				     (uint64_t)(stop - start + 1) * cxt->sector_size);
    1029  
    1030  	rc = fdisk_info(cxt,
    1031  			_("Created a new partition %d of type '%s' and of size %s."),
    1032  			num, t ? t->name : _("Unknown"), str);
    1033  	free(str);
    1034  	return rc;
    1035  }
    1036  
    1037  #ifdef TEST_PROGRAM
    1038  static int test_ranges(struct fdisk_test *ts, int argc, char *argv[])
    1039  {
    1040  	/*                1  -  3,       6,    8, 9,   11    13 */
    1041  	size_t nums[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
    1042  	size_t numx[] = { 0, 0, 0 };
    1043  	char range[BUFSIZ], *ptr = range;
    1044  	size_t i, len = sizeof(range), begin = 0, run = 0;
    1045  
    1046  	for (i = 0; i < ARRAY_SIZE(nums); i++) {
    1047  		if (!nums[i])
    1048  			continue;
    1049  		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
    1050  	}
    1051  	mk_string_list(ptr, &len, &begin, &run, -1, 0);
    1052  	printf("list: '%s'\n", range);
    1053  
    1054  	ptr = range;
    1055  	len = sizeof(range), begin = 0, run = 0;
    1056  	for (i = 0; i < ARRAY_SIZE(numx); i++) {
    1057  		if (!numx[i])
    1058  			continue;
    1059  		ptr = mk_string_list(ptr, &len, &begin, &run, i, 0);
    1060  	}
    1061  	mk_string_list(ptr, &len, &begin, &run, -1, 0);
    1062  	printf("empty list: '%s'\n", range);
    1063  
    1064  	return 0;
    1065  }
    1066  
    1067  int main(int argc, char *argv[])
    1068  {
    1069  	struct fdisk_test tss[] = {
    1070  	{ "--ranges",  test_ranges,    "generates ranges" },
    1071  	{ NULL }
    1072  	};
    1073  
    1074  	return fdisk_run_test(tss, argc, argv);
    1075  }
    1076  
    1077  #endif