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