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