8 * @short_description: interface for dialog driven partitioning
12 static void fdisk_ask_menu_reset_items(struct fdisk_ask
*ask
);
19 * @data: callback data
21 * Set callback for dialog driven partitioning and library warnings/errors.
23 * Returns: 0 on success, < 0 on error.
25 int fdisk_set_ask(struct fdisk_context
*cxt
,
26 int (*ask_cb
)(struct fdisk_context
*, struct fdisk_ask
*, void *),
36 struct fdisk_ask
*fdisk_new_ask(void)
38 struct fdisk_ask
*ask
= calloc(1, sizeof(struct fdisk_ask
));
39 DBG(ASK
, ul_debugobj(ask
, "alloc"));
44 void fdisk_reset_ask(struct fdisk_ask
*ask
)
51 DBG(ASK
, ul_debugobj(ask
, "reset"));
52 refcount
= ask
->refcount
;
54 if (fdisk_is_ask(ask
, MENU
))
55 fdisk_ask_menu_reset_items(ask
);
57 memset(ask
, 0, sizeof(*ask
));
58 ask
->refcount
= refcount
;
65 * Incremparts reference counter.
67 void fdisk_ref_ask(struct fdisk_ask
*ask
)
78 * De-incremparts reference counter, on zero the @ask is automatically
81 void fdisk_unref_ask(struct fdisk_ask
*ask
)
87 if (ask
->refcount
<= 0) {
89 DBG(ASK
, ul_debugobj(ask
, "free"));
95 * fdisk_ask_get_query:
98 * Returns: pointer to dialog string.
100 const char *fdisk_ask_get_query(struct fdisk_ask
*ask
)
106 int fdisk_ask_set_query(struct fdisk_ask
*ask
, const char *str
)
109 return !strdup_to_struct_member(ask
, query
, str
) ? -ENOMEM
: 0;
113 * fdisk_ask_get_type:
116 * Returns: FDISK_ASKTYPE_*
118 int fdisk_ask_get_type(struct fdisk_ask
*ask
)
124 int fdisk_ask_set_type(struct fdisk_ask
*ask
, int type
)
131 int fdisk_do_ask(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
)
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" :
146 DBG(ASK
, ul_debugobj(ask
, "no ask callback specified!"));
150 rc
= cxt
->ask_cb(cxt
, ask
, cxt
->ask_data
);
152 DBG(ASK
, ul_debugobj(ask
, "do_ask done [rc=%d]", rc
));
156 #define is_number_ask(a) (fdisk_is_ask(a, NUMBER) || fdisk_is_ask(a, OFFSET))
159 * fdisk_ask_number_get_range:
162 * Returns: string with range (e.g. "1,3,5-10")
164 const char *fdisk_ask_number_get_range(struct fdisk_ask
*ask
)
167 assert(is_number_ask(ask
));
168 return ask
->data
.num
.range
;
171 int fdisk_ask_number_set_range(struct fdisk_ask
*ask
, const char *range
)
174 assert(is_number_ask(ask
));
175 ask
->data
.num
.range
= range
;
180 * fdisk_ask_number_get_default:
183 * Returns: default number
186 uint64_t fdisk_ask_number_get_default(struct fdisk_ask
*ask
)
189 assert(is_number_ask(ask
));
190 return ask
->data
.num
.dfl
;
193 int fdisk_ask_number_set_default(struct fdisk_ask
*ask
, uint64_t dflt
)
196 ask
->data
.num
.dfl
= dflt
;
201 * fdisk_ask_number_get_low"
204 * Returns: minimal possible number when ask for numbers in range
206 uint64_t fdisk_ask_number_get_low(struct fdisk_ask
*ask
)
209 assert(is_number_ask(ask
));
210 return ask
->data
.num
.low
;
213 int fdisk_ask_number_set_low(struct fdisk_ask
*ask
, uint64_t low
)
216 ask
->data
.num
.low
= low
;
221 * fdisk_ask_number_get_high"
224 * Returns: maximal possible number when ask for numbers in range
226 uint64_t fdisk_ask_number_get_high(struct fdisk_ask
*ask
)
229 assert(is_number_ask(ask
));
230 return ask
->data
.num
.hig
;
233 int fdisk_ask_number_set_high(struct fdisk_ask
*ask
, uint64_t high
)
236 ask
->data
.num
.hig
= high
;
241 * fdisk_ask_number_get_result:
246 uint64_t fdisk_ask_number_get_result(struct fdisk_ask
*ask
)
249 assert(is_number_ask(ask
));
250 return ask
->data
.num
.result
;
254 * fdisk_ask_number_set_result:
256 * @ask: dialog result
258 * Returns: 0 on success, <0 on error
260 int fdisk_ask_number_set_result(struct fdisk_ask
*ask
, uint64_t result
)
263 ask
->data
.num
.result
= result
;
268 * fdisk_ask_number_get_base:
271 * Returns: base when user specify number in relative notation (+<size>)
273 uint64_t fdisk_ask_number_get_base(struct fdisk_ask
*ask
)
276 assert(is_number_ask(ask
));
277 return ask
->data
.num
.base
;
280 int fdisk_ask_number_set_base(struct fdisk_ask
*ask
, uint64_t base
)
283 ask
->data
.num
.base
= base
;
288 * fdisk_ask_number_get_unit:
291 * Returns: number of bytes per the unit
293 uint64_t fdisk_ask_number_get_unit(struct fdisk_ask
*ask
)
296 assert(is_number_ask(ask
));
297 return ask
->data
.num
.unit
;
300 int fdisk_ask_number_set_unit(struct fdisk_ask
*ask
, uint64_t unit
)
303 ask
->data
.num
.unit
= unit
;
307 int fdisk_ask_number_is_relative(struct fdisk_ask
*ask
)
310 assert(is_number_ask(ask
));
311 return ask
->data
.num
.relative
;
315 * fdisk_ask_number_set_relative
319 * Inform libfdisk that user specified number in relative notation rather than
320 * by explicit number. This info allows to fdisk do some optimization (e.g.
321 * align end of partiton, etc.)
323 * Returns: 0 on success, <0 on error
325 int fdisk_ask_number_set_relative(struct fdisk_ask
*ask
, int relative
)
328 ask
->data
.num
.relative
= relative
? 1 : 0;
333 * fdisk_ask_number_inchars:
336 * For example for BSD is normal to address partition by chars rather than by
337 * number (first partition is 'a').
339 * Returns: 1 if number should be presented as chars
342 int fdisk_ask_number_inchars(struct fdisk_ask
*ask
)
345 assert(is_number_ask(ask
));
346 return ask
->data
.num
.inchars
;
350 * Generates string with list ranges (e.g. 1,2,5-8) for the 'cur'
352 #define tochar(num) ((int) ('a' + num - 1))
353 static char *mk_string_list(char *ptr
, size_t *len
, size_t *begin
,
354 size_t *run
, ssize_t cur
, int inchar
)
359 if (!*begin
) { /* begin of the list */
364 if (*begin
+ *run
== cur
) { /* no gap, continue */
368 } else if (!*begin
) {
370 return ptr
; /* end of empty list */
373 /* add to the list */
375 rlen
= inchar
? snprintf(ptr
, *len
, "%c,", tochar(*begin
)) :
376 snprintf(ptr
, *len
, "%zu,", *begin
);
379 snprintf(ptr
, *len
, "%c,%c,", tochar(*begin
), tochar(*begin
+ 1)) :
380 snprintf(ptr
, *len
, "%zu,%zu,", *begin
, *begin
+ 1);
383 snprintf(ptr
, *len
, "%c-%c,", tochar(*begin
), tochar(*begin
+ *run
)) :
384 snprintf(ptr
, *len
, "%zu-%zu,", *begin
, *begin
+ *run
);
386 if (rlen
< 0 || (size_t) rlen
+ 1 > *len
)
391 if (rlen
> 0 && *len
> (size_t) rlen
)
396 if (cur
== -1 && *begin
) {
397 /* end of the list */
398 *(ptr
- 1) = '\0'; /* remove tailing ',' from the list */
411 * @partnum: returns partition number
414 * High-level API to ask for used or unused partition number.
416 * Returns: 0 on success, < 0 on error, 1 if no free/used partition
418 int fdisk_ask_partnum(struct fdisk_context
*cxt
, size_t *partnum
, int wantnew
)
420 int rc
= 0, inchar
= 0;
421 char range
[BUFSIZ
], *ptr
= range
;
422 size_t i
, len
= sizeof(range
), begin
= 0, run
= 0;
423 struct fdisk_ask
*ask
= NULL
;
424 __typeof__(ask
->data
.num
) *num
;
430 if (cxt
->label
&& cxt
->label
->flags
& FDISK_LABEL_FL_INCHARS_PARTNO
)
433 DBG(ASK
, ul_debug("%s: asking for %s partition number "
434 "(max: %zu, inchar: %s)",
436 wantnew
? "new" : "used",
437 cxt
->label
->nparts_max
,
438 inchar
? "yes" : "not"));
440 ask
= fdisk_new_ask();
444 fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
445 num
= &ask
->data
.num
;
447 ask
->data
.num
.inchars
= inchar
? 1 : 0;
449 for (i
= 0; i
< cxt
->label
->nparts_max
; i
++) {
450 int used
= fdisk_is_partition_used(cxt
, i
);
452 if (wantnew
&& !used
) {
453 ptr
= mk_string_list(ptr
, &len
, &begin
, &run
, i
, inchar
);
459 num
->dfl
= num
->low
= i
+ 1;
461 } else if (!wantnew
&& used
) {
462 ptr
= mk_string_list(ptr
, &len
, &begin
, &run
, i
, inchar
);
465 num
->dfl
= num
->hig
= i
+ 1;
469 DBG(ASK
, ul_debugobj(ask
, "ask limits: low: %ju, high: %ju, default: %ju",
470 num
->low
, num
->hig
, num
->dfl
));
472 if (!rc
&& !wantnew
&& num
->low
== num
->hig
) {
474 /* only one existing partiton, don't ask, return the number */
475 fdisk_ask_number_set_result(ask
, num
->low
);
476 fdisk_info(cxt
, _("Selected partition %ju"), num
->low
);
478 } else if (num
->low
== 0) {
479 fdisk_warnx(cxt
, _("No partition is defined yet!"));
484 if (!rc
&& wantnew
&& num
->low
== num
->hig
) {
486 /* only one free partition, don't ask, return the number */
487 fdisk_ask_number_set_result(ask
, num
->low
);
488 fdisk_info(cxt
, _("Selected partition %ju"), num
->low
);
491 fdisk_warnx(cxt
, _("No free partition available!"));
497 mk_string_list(ptr
, &len
, &begin
, &run
, -1, inchar
); /* terminate the list */
498 rc
= fdisk_ask_number_set_range(ask
, range
);
501 rc
= fdisk_ask_set_query(ask
, _("Partition number"));
503 rc
= fdisk_do_ask(cxt
, ask
);
507 *partnum
= fdisk_ask_number_get_result(ask
);
511 DBG(ASK
, ul_debugobj(ask
, "result: %ju [rc=%d]\n", fdisk_ask_number_get_result(ask
), rc
));
512 fdisk_unref_ask(ask
);
519 * @low: minimal possible number
520 * @dflt: default suggestion
521 * @high: maximal possible number
522 * @query: question string
523 * @result: returns result
525 * Returns: 0 on success, <0 on error.
527 int fdisk_ask_number(struct fdisk_context
*cxt
,
534 struct fdisk_ask
*ask
;
539 ask
= fdisk_new_ask();
543 rc
= fdisk_ask_set_type(ask
, FDISK_ASKTYPE_NUMBER
);
545 fdisk_ask_number_set_low(ask
, low
);
547 fdisk_ask_number_set_default(ask
, dflt
);
549 fdisk_ask_number_set_high(ask
, high
);
551 fdisk_ask_set_query(ask
, query
);
553 rc
= fdisk_do_ask(cxt
, ask
);
555 *result
= fdisk_ask_number_get_result(ask
);
557 DBG(ASK
, ul_debugobj(ask
, "result: %ju [rc=%d]\n", *result
, rc
));
558 fdisk_unref_ask(ask
);
563 * fdisk_ask_string_get_result:
566 * Returns: pointer to dialog result
568 char *fdisk_ask_string_get_result(struct fdisk_ask
*ask
)
571 assert(fdisk_is_ask(ask
, STRING
));
572 return ask
->data
.str
.result
;
576 * fdisk_ask_string_set_result:
578 * @result: pointer to allocated buffer with string
580 * You don't have to care about the @result deallocation, libfdisk is going to
581 * deallocate the result when destroy @ask instance.
583 * Returns: 0 on success, <0 on error
585 int fdisk_ask_string_set_result(struct fdisk_ask
*ask
, char *result
)
588 ask
->data
.str
.result
= result
;
595 * @query: question string
596 * @result: returns allocated buffer
598 * High-level API to ask for strings. Don't forget to deallocate the @result.
600 * Returns: 0 on success, <0 on error.
602 int fdisk_ask_string(struct fdisk_context
*cxt
,
606 struct fdisk_ask
*ask
;
611 ask
= fdisk_new_ask();
615 rc
= fdisk_ask_set_type(ask
, FDISK_ASKTYPE_STRING
);
617 fdisk_ask_set_query(ask
, query
);
619 rc
= fdisk_do_ask(cxt
, ask
);
621 *result
= fdisk_ask_string_get_result(ask
);
623 DBG(ASK
, ul_debugobj(ask
, "result: %s [rc=%d]\n", *result
, rc
));
624 fdisk_unref_ask(ask
);
631 * @query: question string
632 * @result: returns 0 (no) or 1 (yes)
634 * Hight-level API to ask Yes/No questions
636 * Returns: 0 on success, <0 on error
638 int fdisk_ask_yesno(struct fdisk_context
*cxt
,
642 struct fdisk_ask
*ask
;
647 ask
= fdisk_new_ask();
651 rc
= fdisk_ask_set_type(ask
, FDISK_ASKTYPE_YESNO
);
653 fdisk_ask_set_query(ask
, query
);
655 rc
= fdisk_do_ask(cxt
, ask
);
657 *result
= fdisk_ask_yesno_get_result(ask
) == 1 ? 1 : 0;
659 DBG(ASK
, ul_debugobj(ask
, "result: %d [rc=%d]\n", *result
, rc
));
660 fdisk_unref_ask(ask
);
665 * fdisk_ask_yesno_get_result:
670 int fdisk_ask_yesno_get_result(struct fdisk_ask
*ask
)
673 assert(fdisk_is_ask(ask
, YESNO
));
674 return ask
->data
.yesno
.result
;
678 * fdisk_ask_yesno_set_result:
682 * Returns: 0 on success, <0 on error
684 int fdisk_ask_yesno_set_result(struct fdisk_ask
*ask
, int result
)
687 ask
->data
.yesno
.result
= result
;
694 int fdisk_ask_menu_set_default(struct fdisk_ask
*ask
, int dfl
)
697 assert(fdisk_is_ask(ask
, MENU
));
698 ask
->data
.menu
.dfl
= dfl
;
703 * fdisk_ask_menu_get_default:
706 * Returns: default menu item key
708 int fdisk_ask_menu_get_default(struct fdisk_ask
*ask
)
711 assert(fdisk_is_ask(ask
, MENU
));
712 return ask
->data
.menu
.dfl
;
716 * fdisk_ask_menu_set_result:
720 * Returns: 0 on success, <0 on error
722 int fdisk_ask_menu_set_result(struct fdisk_ask
*ask
, int key
)
725 assert(fdisk_is_ask(ask
, MENU
));
726 ask
->data
.menu
.result
= key
;
727 DBG(ASK
, ul_debugobj(ask
, "menu result: %c\n", key
));
733 * fdisk_ask_menu_get_result:
735 * @key: returns selected menu item key
737 * Returns: 0 on success, <0 on error.
739 int fdisk_ask_menu_get_result(struct fdisk_ask
*ask
, int *key
)
742 assert(fdisk_is_ask(ask
, MENU
));
744 *key
= ask
->data
.menu
.result
;
749 * fdisk_ask_menu_get_item:
750 * @ask: ask menu instance
751 * @idx: wanted menu item index
752 * @key: returns key of the menu item
753 * @name: returns name of the menu item
754 * @desc: returns description of the menu item
756 * Returns: 0 on success, <0 on error, >0 if idx out-of-range
758 int fdisk_ask_menu_get_item(struct fdisk_ask
*ask
, size_t idx
, int *key
,
759 const char **name
, const char **desc
)
762 struct ask_menuitem
*mi
;
765 assert(fdisk_is_ask(ask
, MENU
));
767 for (i
= 0, mi
= ask
->data
.menu
.first
; mi
; mi
= mi
->next
, i
++) {
773 return 1; /* no more items */
783 static void fdisk_ask_menu_reset_items(struct fdisk_ask
*ask
)
785 struct ask_menuitem
*mi
;
788 assert(fdisk_is_ask(ask
, MENU
));
790 for (mi
= ask
->data
.menu
.first
; mi
; ) {
791 struct ask_menuitem
*next
= mi
->next
;
798 * fdisk_ask_menu_get_nitems:
801 * Returns: number of menu items
803 size_t fdisk_ask_menu_get_nitems(struct fdisk_ask
*ask
)
805 struct ask_menuitem
*mi
;
809 assert(fdisk_is_ask(ask
, MENU
));
811 for (n
= 0, mi
= ask
->data
.menu
.first
; mi
; mi
= mi
->next
, n
++);
816 int fdisk_ask_menu_add_item(struct fdisk_ask
*ask
, int key
,
817 const char *name
, const char *desc
)
819 struct ask_menuitem
*mi
;
822 assert(fdisk_is_ask(ask
, MENU
));
824 mi
= calloc(1, sizeof(*mi
));
831 if (!ask
->data
.menu
.first
)
832 ask
->data
.menu
.first
= mi
;
834 struct ask_menuitem
*last
= ask
->data
.menu
.first
;
841 DBG(ASK
, ul_debugobj(ask
, "new menu item: %c, \"%s\" (%s)\n", mi
->key
, mi
->name
, mi
->desc
));
850 #define is_print_ask(a) (fdisk_is_ask(a, WARN) || fdisk_is_ask(a, WARNX) || fdisk_is_ask(a, INFO))
853 * fdisk_ask_print_get_errno:
856 * Returns: error number for warning/error messages
858 int fdisk_ask_print_get_errno(struct fdisk_ask
*ask
)
861 assert(is_print_ask(ask
));
862 return ask
->data
.print
.errnum
;
865 int fdisk_ask_print_set_errno(struct fdisk_ask
*ask
, int errnum
)
868 ask
->data
.print
.errnum
= errnum
;
873 * fdisk_ask_print_get_mesg:
876 * Returns: pointer to message
878 const char *fdisk_ask_print_get_mesg(struct fdisk_ask
*ask
)
881 assert(is_print_ask(ask
));
882 return ask
->data
.print
.mesg
;
885 /* does not reallocate the message! */
886 int fdisk_ask_print_set_mesg(struct fdisk_ask
*ask
, const char *mesg
)
889 ask
->data
.print
.mesg
= mesg
;
893 static int do_vprint(struct fdisk_context
*cxt
, int errnum
, int type
,
894 const char *fmt
, va_list va
)
896 struct fdisk_ask
*ask
;
902 if (vasprintf(&mesg
, fmt
, va
) < 0)
905 ask
= fdisk_new_ask();
911 fdisk_ask_set_type(ask
, type
);
912 fdisk_ask_print_set_mesg(ask
, mesg
);
914 fdisk_ask_print_set_errno(ask
, errnum
);
915 rc
= fdisk_do_ask(cxt
, ask
);
917 fdisk_unref_ask(ask
);
925 * @fmt: printf-like formatted string
927 * High-level API to print info messages,
929 * Returns: 0 on success, <0 on error
931 int fdisk_info(struct fdisk_context
*cxt
, const char *fmt
, ...)
938 rc
= do_vprint(cxt
, -1, FDISK_ASKTYPE_INFO
, fmt
, ap
);
946 * @fmt: printf-like formatted string
948 * High-level API to print warning message (errno expected)
950 * Returns: 0 on success, <0 on error
952 int fdisk_warn(struct fdisk_context
*cxt
, const char *fmt
, ...)
959 rc
= do_vprint(cxt
, errno
, FDISK_ASKTYPE_WARN
, fmt
, ap
);
967 * @fmt: printf-like formatted string
969 * High-level API to print warning message
971 * Returns: 0 on success, <0 on error
973 int fdisk_warnx(struct fdisk_context
*cxt
, const char *fmt
, ...)
980 rc
= do_vprint(cxt
, -1, FDISK_ASKTYPE_WARNX
, fmt
, ap
);
985 int fdisk_info_new_partition(
986 struct fdisk_context
*cxt
,
987 int num
, sector_t start
, sector_t stop
,
988 struct fdisk_parttype
*t
)
991 char *str
= size_to_human_string(SIZE_SUFFIX_3LETTER
| SIZE_SUFFIX_SPACE
,
992 (uint64_t)(stop
- start
+ 1) * cxt
->sector_size
);
995 _("Created a new partition %d of type '%s' and of size %s."),
996 num
, t
? t
->name
: _("Unknown"), str
);
1002 int test_ranges(struct fdisk_test
*ts
, int argc
, char *argv
[])
1004 /* 1 - 3, 6, 8, 9, 11 13 */
1005 size_t nums
[] = { 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1 };
1006 size_t numx
[] = { 0, 0, 0 };
1007 char range
[BUFSIZ
], *ptr
= range
;
1008 size_t i
, len
= sizeof(range
), begin
= 0, run
= 0;
1010 for (i
= 0; i
< ARRAY_SIZE(nums
); i
++) {
1013 ptr
= mk_string_list(ptr
, &len
, &begin
, &run
, i
, 0);
1015 mk_string_list(ptr
, &len
, &begin
, &run
, -1, 0);
1016 printf("list: '%s'\n", range
);
1019 len
= sizeof(range
), begin
= 0, run
= 0;
1020 for (i
= 0; i
< ARRAY_SIZE(numx
); i
++) {
1023 ptr
= mk_string_list(ptr
, &len
, &begin
, &run
, i
, 0);
1025 mk_string_list(ptr
, &len
, &begin
, &run
, -1, 0);
1026 printf("empty list: '%s'\n", range
);
1031 int main(int argc
, char *argv
[])
1033 struct fdisk_test tss
[] = {
1034 { "--ranges", test_ranges
, "generates ranges" },
1038 return fdisk_run_test(tss
, argc
, argv
);