Coverage Report

Created: 2025-08-29 06:14

/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