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