]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/cfdisk.c
cfdisk: add UI for linfdisk menus, ask for size
[thirdparty/util-linux.git] / fdisks / cfdisk.c
CommitLineData
6dbe3af9
KZ
1#include <stdlib.h>
2#include <stdio.h>
6dbe3af9 3#include <errno.h>
8c3a5a44 4#include <signal.h>
00b4f26a 5#include <ctype.h>
541e6934
KZ
6
7#ifdef HAVE_SLANG_H
8#include <slang.h>
9#elif defined(HAVE_SLANG_SLANG_H)
10#include <slang/slang.h>
11#endif
12
48d7b13a
KZ
13#ifdef HAVE_SLCURSES_H
14#include <slcurses.h>
15#elif defined(HAVE_SLANG_SLCURSES_H)
16#include <slang/slcurses.h>
30c97bb8
MF
17#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
18#include <ncursesw/ncurses.h>
48d7b13a
KZ
19#elif defined(HAVE_NCURSES_H)
20#include <ncurses.h>
21#elif defined(HAVE_NCURSES_NCURSES_H)
22#include <ncurses/ncurses.h>
2b6fc908 23#endif
541e6934 24
5f94ca33
KZ
25#ifdef HAVE_WIDECHAR
26#include <wctype.h>
27#endif
28
8c3a5a44 29#include "c.h"
b2d28533 30#include "closestream.h"
7eda085c 31#include "nls.h"
8abcf290 32#include "strutils.h"
8c3a5a44 33#include "xalloc.h"
5f94ca33 34#include "mbsalign.h"
6dbe3af9 35
8c3a5a44
KZ
36#include "fdiskP.h"
37
38#define ARROW_CURSOR_STRING ">>> "
39#define ARROW_CURSOR_DUMMY " "
40#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
41
42#define MENU_PADDING 2
43#define TABLE_START_LINE 4
44#define MENU_START_LINE (LINES - 5)
1af8003b 45#define INFO_LINE (LINES - 2)
b1f58330
KZ
46#define HINT_LINE (LINES - 1)
47
48#define CFDISK_ERR_ESC 5000
49
50#ifndef KEY_ESC
51# define KEY_ESC '\033'
52#endif
53#ifndef KEY_DELETE
54# define KEY_DELETE '\177'
55#endif
1af8003b
KZ
56
57/* colors */
58enum {
59 CFDISK_CL_NONE = 0,
60 CFDISK_CL_WARNING
61};
62static const int color_pairs[][2] = {
63 /* color foreground, background */
64 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
65};
8c3a5a44 66
8460875d
KZ
67struct cfdisk;
68typedef int (menu_callback_t)(struct cfdisk *, int);
69
70static int menu_cb_main(struct cfdisk *cf, int key);
83fa0f80
KZ
71static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx);
72static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
b1f58330
KZ
73static struct cfdisk_menu *menu_push(struct cfdisk *cf, size_t id, struct cfdisk_menudesc *desc);
74static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
83fa0f80 75
1af8003b 76static int ui_refresh(struct cfdisk *cf);
83fa0f80
KZ
77static void ui_warnx(const char *fmt, ...);
78static void ui_warn(const char *fmt, ...);
79static void ui_info(const char *fmt, ...);
b1f58330
KZ
80static void ui_draw_menu(struct cfdisk *cf);
81static void ui_menu_goto(struct cfdisk *cf, int where);
82static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
83 uintmax_t low, uintmax_t up);
8460875d 84
83fa0f80 85static int ui_enabled;
8c3a5a44
KZ
86
87struct cfdisk_menudesc {
88 int key; /* keyboard shortcut */
89 const char *name; /* item name */
90 const char *desc; /* item description */
749af4b6
KZ
91};
92
8c3a5a44
KZ
93struct cfdisk_menu {
94 struct cfdisk_menudesc *desc;
95 char *ignore;
96 size_t id;
97 size_t width;
98 size_t nitems;
8460875d
KZ
99
100 menu_callback_t *callback;
101
8c3a5a44 102 struct cfdisk_menu *prev;
749af4b6
KZ
103};
104
8c3a5a44
KZ
105static struct cfdisk_menudesc menu_main[] = {
106 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
107 { 'd', N_("Delete"), N_("Delete the current partition") },
108// { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
109// { 'h', N_("Help"), N_("Print help screen") },
110// { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
111 { 'n', N_("New"), N_("Create new partition from free space") },
112// { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
113 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
114 { 't', N_("Type"), N_("Change the partition type") },
115// { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
116 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
117 { 0, NULL, NULL }
118};
6dbe3af9 119
8c3a5a44 120enum {
b1f58330
KZ
121 CFDISK_MENU_GENERATED = -1, /* used in libfdisk callback */
122
123 /* built-in menus */
8c3a5a44 124 CFDISK_MENU_MAIN = 0,
b1f58330 125
5c36a0eb
KZ
126};
127
8c3a5a44
KZ
128static struct cfdisk_menudesc *menus[] = {
129 [CFDISK_MENU_MAIN] = menu_main
130};
7eda085c 131
8460875d
KZ
132static menu_callback_t *menu_callbacks[] = {
133 [CFDISK_MENU_MAIN] = menu_cb_main
134};
135
136
8c3a5a44
KZ
137struct cfdisk {
138 struct fdisk_context *cxt; /* libfdisk context */
139 struct fdisk_table *table; /* partition table */
7eda085c 140
8c3a5a44
KZ
141 struct cfdisk_menu *menu; /* the current menu */
142 size_t menu_idx;
7eda085c 143
8c3a5a44
KZ
144 int *cols; /* output columns */
145 size_t ncols; /* number of columns */
7eda085c 146
8c3a5a44
KZ
147 char *linesbuf; /* table as string */
148 size_t linesbufsz; /* size of the tb_buf */
7eda085c 149
8c3a5a44
KZ
150 char **lines; /* array with lines */
151 size_t nlines; /* number of lines */
152 size_t lines_idx; /* current line <0..N>, exclude header */
8c3a5a44 153};
5c36a0eb 154
8c3a5a44
KZ
155static int cols_init(struct cfdisk *cf)
156{
157 assert(cf);
5c36a0eb 158
8c3a5a44
KZ
159 free(cf->cols);
160 cf->cols = NULL;
161 cf->ncols = 0;
5c36a0eb 162
8c3a5a44 163 return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols);
5c36a0eb
KZ
164}
165
8c3a5a44
KZ
166/* It would be possible to use fdisk_table_to_string(), but we want some
167 * extension to the output format, so let's do it without libfdisk
168 */
169static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
170{
171 struct fdisk_partition *pa;
172 const struct fdisk_column *col;
173 struct fdisk_label *lb;
174 struct fdisk_iter *itr = NULL;
175 struct tt *tt = NULL;
176 char *res = NULL;
177 size_t i;
178
179 DBG(FRONTEND, dbgprint("table: convert to string"));
180
181 assert(cf);
182 assert(cf->cxt);
183 assert(cf->cols);
184 assert(tb);
185
186 lb = fdisk_context_get_label(cf->cxt, NULL);
187 assert(lb);
188
189 tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX);
190 if (!tt)
191 goto done;
192 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
193 if (!itr)
194 goto done;
5c36a0eb 195
8c3a5a44
KZ
196 /* headers */
197 for (i = 0; i < cf->ncols; i++) {
198 col = fdisk_label_get_column(lb, cf->cols[i]);
199 if (col)
200 tt_define_column(tt, col->name,
201 col->width,
202 col->tt_flags);
203 }
204
205 /* data */
206 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
207 struct tt_line *ln = tt_add_line(tt, NULL);
208 if (!ln)
209 goto done;
210 for (i = 0; i < cf->ncols; i++) {
211 char *cdata = NULL;
212
213 col = fdisk_label_get_column(lb, cf->cols[i]);
214 if (!col)
215 continue;
216 if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata))
217 continue;
218 tt_line_set_data(ln, i, cdata);
219 }
220 }
6dbe3af9 221
8c3a5a44
KZ
222 if (!tt_is_empty(tt)) {
223 tt_set_termreduce(tt, ARROW_CURSOR_WIDTH);
224 tt_print_table_to_string(tt, &res);
225 }
226done:
227 tt_free_table(tt);
228 fdisk_free_iter(itr);
6dbe3af9 229
8c3a5a44 230 return res;
2b6fc908
KZ
231}
232
8460875d 233static int lines_refresh(struct cfdisk *cf)
8c3a5a44
KZ
234{
235 int rc;
236 char *p;
237 size_t i;
7eda085c 238
8c3a5a44 239 assert(cf);
2b6fc908 240
1af8003b 241 DBG(FRONTEND, dbgprint("refreshing buffer"));
c64061c9 242
8c3a5a44
KZ
243 free(cf->linesbuf);
244 free(cf->lines);
245 cf->linesbuf = NULL;
246 cf->linesbufsz = 0;
247 cf->lines = NULL;
248 cf->nlines = 0;
6dbe3af9 249
8c3a5a44 250 fdisk_unref_table(cf->table);
1af8003b 251 cf->table = NULL;
8c3a5a44 252 fdisk_context_enable_freespace(cf->cxt, 1);
6dbe3af9 253
8c3a5a44
KZ
254 rc = fdisk_get_table(cf->cxt, &cf->table);
255 if (rc)
256 return rc;
0d8589c5 257
8c3a5a44
KZ
258 cf->linesbuf = table_to_string(cf, cf->table);
259 if (!cf->linesbuf)
260 return -ENOMEM;
6dbe3af9 261
8c3a5a44
KZ
262 cf->linesbufsz = strlen(cf->linesbuf);
263 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
6dbe3af9 264
8c3a5a44
KZ
265 cf->lines = calloc(cf->nlines, sizeof(char *));
266 if (!cf->lines)
267 return -ENOMEM;
6dbe3af9 268
8c3a5a44
KZ
269 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
270 cf->lines[i] = p;
271 p = strchr(p, '\n');
272 if (p) {
273 *p = '\0';
274 p++;
275 }
276 }
6dbe3af9 277
8c3a5a44 278 return 0;
6dbe3af9
KZ
279}
280
00b4f26a
KZ
281static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
282{
283 assert(cf);
284 assert(cf->table);
285
286 return fdisk_table_get_partition(cf->table, cf->lines_idx);
287}
288
b1f58330
KZ
289/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
290 * responseback to libfdisk
291 */
292static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
293{
294 struct cfdisk_menudesc *d, *cm;
295 int key;
296 size_t i = 0, nitems;
297 const char *name, *desc;
298
299 assert(ask);
300 assert(cf);
301
302 /* create cfdisk menu according to libfdisk ask-menu, note that the
303 * last cm[] item has to be empty -- so nitems + 1 */
304 nitems = fdisk_ask_menu_get_nitems(ask);
305 cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
306 if (!cm)
307 return -ENOMEM;
308
309 for (i = 0; i < nitems; i++) {
310 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
311 break;
312 cm[i].key = key;
313 cm[i].desc = desc;
314 cm[i].name = name;
315 }
316
317 /* make the new menu active */
318 menu_push(cf, CFDISK_MENU_GENERATED, cm);
319 ui_draw_menu(cf);
320 refresh();
321
322 /* wait for keys */
323 do {
324 switch (getch()) {
325 case KEY_LEFT:
326#ifdef KEY_BTAB
327 case KEY_BTAB:
328#endif
329 ui_menu_goto(cf, cf->menu_idx - 1);
330 break;
331 case KEY_RIGHT:
332 case '\t':
333 ui_menu_goto(cf, cf->menu_idx + 1);
334 break;
335 case KEY_ENTER:
336 case '\n':
337 case '\r':
338 d = menu_get_menuitem(cf, cf->menu_idx);
339 if (d)
340 fdisk_ask_menu_set_result(ask, d->key);
341 menu_pop(cf);
342 free(cm);
343 return 0;
344 }
345 } while (1);
346
347 menu_pop(cf);
348 free(cm);
349 return -1;
350}
351
352
8c3a5a44
KZ
353static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
354 void *data __attribute__((__unused__)))
355{
356 int rc = 0;
6dbe3af9 357
8c3a5a44
KZ
358 assert(cxt);
359 assert(ask);
6dbe3af9 360
8c3a5a44
KZ
361 switch(fdisk_ask_get_type(ask)) {
362 case FDISK_ASKTYPE_INFO:
83fa0f80 363 ui_info(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
364 break;
365 case FDISK_ASKTYPE_WARNX:
83fa0f80 366 ui_warnx(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
367 break;
368 case FDISK_ASKTYPE_WARN:
83fa0f80 369 ui_warn(fdisk_ask_print_get_mesg(ask));
8c3a5a44 370 break;
b1f58330
KZ
371 case FDISK_ASKTYPE_MENU:
372 ask_menu(ask, (struct cfdisk *) data);
373 break;
8c3a5a44 374 default:
83fa0f80
KZ
375 ui_warnx(_("internal error: unsupported dialog type %d"),
376 fdisk_ask_get_type(ask));
8c3a5a44
KZ
377 return -EINVAL;
378 }
379 return rc;
fd6b7a7f 380}
6dbe3af9 381
6dbe3af9 382
8c3a5a44
KZ
383static int ui_end(struct cfdisk *cf)
384{
83fa0f80 385 if (cf && !ui_enabled)
8c3a5a44
KZ
386 return -EINVAL;
387
48d7b13a 388#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
8c3a5a44
KZ
389 SLsmg_gotorc(LINES - 1, 0);
390 SLsmg_refresh();
2b6fc908 391#else
8c3a5a44 392 mvcur(0, COLS - 1, LINES-1, 0);
2b6fc908 393#endif
8c3a5a44
KZ
394 nl();
395 endwin();
396 printf("\n");
397 return 0;
6dbe3af9
KZ
398}
399
1af8003b 400static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap)
f609e92e 401{
8c3a5a44 402 size_t width;
8c3a5a44 403 char *buf = NULL;
f609e92e 404
8c3a5a44
KZ
405 move(line, 0);
406 clrtoeol();
f609e92e 407
8c3a5a44 408 xvasprintf(&buf, fmt, ap);
fd6b7a7f 409
b1f58330 410 width = mbs_safe_width(buf);
1af8003b
KZ
411
412 attron(attrs);
8c3a5a44 413 mvaddstr(line, (COLS - width) / 2, buf);
1af8003b 414 attroff(attrs);
8c3a5a44 415 free(buf);
6dbe3af9
KZ
416}
417
1af8003b
KZ
418static void ui_center(int line, const char *fmt, ...)
419{
420 va_list ap;
421 va_start(ap, fmt);
422 ui_vprint_center(line, 0, fmt, ap);
423 va_end(ap);
424}
425
83fa0f80
KZ
426static void ui_warnx(const char *fmt, ...)
427{
428 va_list ap;
429 va_start(ap, fmt);
430 if (ui_enabled)
431 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt, ap);
432 else
433 vfprintf(stderr, fmt, ap);
434 va_end(ap);
435}
436
437static void ui_warn(const char *fmt, ...)
1af8003b 438{
83fa0f80 439 char *fmt_m;
1af8003b 440 va_list ap;
83fa0f80
KZ
441
442 xasprintf(&fmt_m, "%s: %m", fmt);
443
1af8003b 444 va_start(ap, fmt);
83fa0f80
KZ
445 if (ui_enabled)
446 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt_m, ap);
447 else
448 vfprintf(stderr, fmt_m, ap);
1af8003b 449 va_end(ap);
83fa0f80 450 free(fmt_m);
1af8003b
KZ
451}
452
453static void ui_info(const char *fmt, ...)
454{
455 va_list ap;
456 va_start(ap, fmt);
83fa0f80
KZ
457 if (ui_enabled)
458 ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap);
459 else
460 vfprintf(stdout, fmt, ap);
1af8003b
KZ
461 va_end(ap);
462}
463
464static void ui_clean_info(void)
465{
466 move(INFO_LINE, 0);
467 clrtoeol();
468}
6dbe3af9 469
b1f58330
KZ
470static void ui_hint(const char *fmt, ...)
471{
472 va_list ap;
473 va_start(ap, fmt);
474 if (ui_enabled)
475 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
476 else
477 vfprintf(stdout, fmt, ap);
478 va_end(ap);
479}
480
481static void ui_clean_hint(void)
482{
483 move(HINT_LINE, 0);
484 clrtoeol();
485}
486
8c3a5a44
KZ
487static void die_on_signal(int dummy __attribute__((__unused__)))
488{
489 ui_end(NULL);
490 exit(EXIT_FAILURE);
6dbe3af9
KZ
491}
492
8c3a5a44
KZ
493static void menu_update_ignore(struct cfdisk *cf)
494{
00b4f26a
KZ
495 char ignore[128] = { 0 };
496 int i = 0;
497 struct fdisk_partition *pa;
8c3a5a44 498 struct cfdisk_menu *m;
83fa0f80
KZ
499 struct cfdisk_menudesc *d, *org;
500 size_t idx;
6dbe3af9 501
8c3a5a44 502 assert(cf);
6dbe3af9 503
8c3a5a44 504 m = cf->menu;
83fa0f80
KZ
505 org = menu_get_menuitem(cf, cf->menu_idx);
506
8c3a5a44 507 DBG(FRONTEND, dbgprint("menu: update menu ignored keys"));
6dbe3af9 508
8c3a5a44
KZ
509 switch (m->id) {
510 case CFDISK_MENU_MAIN:
00b4f26a
KZ
511 pa = get_current_partition(cf);
512 if (!pa)
513 break;
514 if (fdisk_partition_is_freespace(pa)) {
515 ignore[i++] = 'd'; /* delete */
516 ignore[i++] = 't'; /* set type */
517 ignore[i++] = 'b'; /* set bootable */
83fa0f80 518 } else {
00b4f26a 519 ignore[i++] = 'n';
83fa0f80
KZ
520 if (!fdisk_is_disklabel(cf->cxt, DOS) &&
521 !fdisk_is_disklabel(cf->cxt, SGI))
522 ignore[i++] = 'b';
523 }
00b4f26a 524
8c3a5a44 525 break;
fd6b7a7f 526 }
6dbe3af9 527
00b4f26a
KZ
528 ignore[i] = '\0';
529
8c3a5a44 530 /* return if no change */
00b4f26a
KZ
531 if ( (!m->ignore && !*ignore)
532 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
8c3a5a44 533 return;
fd6b7a7f 534 }
6dbe3af9 535
8c3a5a44 536 free(m->ignore);
8460875d 537 m->ignore = xstrdup(ignore);
8c3a5a44 538 m->nitems = 0;
6dbe3af9 539
8c3a5a44
KZ
540 for (d = m->desc; d->name; d++) {
541 if (m->ignore && strchr(m->ignore, d->key))
8460875d
KZ
542 continue;
543 m->nitems++;
8c3a5a44 544 }
83fa0f80
KZ
545
546 /* refresh menu index to be at the same menuitem or go to the first */
547 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
548 cf->menu_idx = idx;
549 else
550 cf->menu_idx = 0;
6dbe3af9
KZ
551}
552
b1f58330
KZ
553static struct cfdisk_menu *menu_push(
554 struct cfdisk *cf,
555 size_t id,
556 struct cfdisk_menudesc *desc)
8c3a5a44
KZ
557{
558 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
559 struct cfdisk_menudesc *d;
6dbe3af9 560
8c3a5a44 561 assert(cf);
6dbe3af9 562
8c3a5a44 563 DBG(FRONTEND, dbgprint("menu: new menu"));
6dbe3af9 564
8c3a5a44
KZ
565 m->prev = cf->menu;
566 m->id = id;
b1f58330 567 m->desc = desc ? desc : menus[id];
8460875d 568 m->callback = menu_callbacks[id];
6dbe3af9 569
8c3a5a44
KZ
570 for (d = m->desc; d->name; d++) {
571 const char *name = _(d->name);
b1f58330 572 size_t len = mbs_safe_width(name);
8c3a5a44
KZ
573 if (len > m->width)
574 m->width = len;
575 m->nitems++;
576 }
6dbe3af9 577
8c3a5a44
KZ
578 cf->menu = m;
579 return m;
6dbe3af9
KZ
580}
581
8c3a5a44 582static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
6dbe3af9 583{
8c3a5a44 584 struct cfdisk_menu *m = NULL;
6dbe3af9 585
8c3a5a44 586 assert(cf);
7eda085c 587
8c3a5a44 588 DBG(FRONTEND, dbgprint("menu: rem menu"));
7eda085c 589
8c3a5a44
KZ
590 if (cf->menu) {
591 m = cf->menu->prev;
592 free(cf->menu->ignore);
593 free(cf->menu);
c07ebfa1 594 }
8c3a5a44
KZ
595 cf->menu = m;
596 return cf->menu;
6dbe3af9
KZ
597}
598
8460875d
KZ
599/* returns: error: < 0, success: 0, quit: 1 */
600static int menu_cb_main(struct cfdisk *cf, int key)
601{
83fa0f80 602 size_t n;
b1f58330 603 int ref = 0, rc;
83fa0f80 604 const char *info = NULL, *warn = NULL;
b1f58330 605 struct fdisk_partition *pa;
1af8003b
KZ
606
607 assert(cf);
608 assert(cf->cxt);
609 assert(key);
610
83fa0f80 611 n = cf->lines_idx; /* the current partition */
b1f58330 612 pa = get_current_partition(cf);
83fa0f80 613
8460875d 614 switch (key) {
83fa0f80
KZ
615 case 'b': /* Bootable flag */
616 {
617 int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
618 fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
619
620 if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl))
621 warn = _("Could not toggle the flag.");
622 else if (fl)
623 ref = 1;
624 break;
625 }
8460875d 626 case 'd': /* Delete */
83fa0f80
KZ
627 if (fdisk_delete_partition(cf->cxt, n) != 0)
628 warn = _("Could not delete partition %zu.");
1af8003b 629 else
83fa0f80
KZ
630 info = _("Partition %zu has been deleted.");
631 ref = 1;
8460875d
KZ
632 break;
633 case 'n': /* New */
b1f58330
KZ
634 {
635 uint64_t start, size;
636 struct fdisk_partition *npa; /* the new partition */
637
638 if (!pa || !fdisk_partition_is_freespace(pa))
639 return -EINVAL;
640 npa = fdisk_new_partition();
641 if (!npa)
642 return -ENOMEM;
643 /* free space range */
644 start = fdisk_partition_get_start(pa);
645 size = fdisk_partition_get_size(pa) * cf->cxt->sector_size;
646
647 if (ui_get_size(cf, _("Partition size: "), &size, 1, size)
648 == -CFDISK_ERR_ESC)
649 break;
650 size /= cf->cxt->sector_size;
651 /* properties of the new partition */
652 fdisk_partition_set_start(npa, start);
653 fdisk_partition_set_size(npa, size);
654 fdisk_partition_partno_follow_default(npa, 1);
655 /* add to disk label -- libfdisk will ask for missing details */
656 rc = fdisk_add_partition(cf->cxt, npa);
657 fdisk_unref_partition(npa);
658 if (rc == 0)
659 ref = 1;
8460875d 660 break;
b1f58330 661 }
8460875d
KZ
662 case 'q': /* Quit */
663 return 1;
664 case 't': /* Type */
665 break;
666 case 'W': /* Write */
667 break;
8460875d 668 }
83fa0f80
KZ
669
670 if (ref) {
671 lines_refresh(cf);
672 ui_refresh(cf);
673 }
674 if (warn)
675 ui_warnx(warn, n);
676 else if (info)
677 ui_info(info, n);
678
8460875d
KZ
679 return 0;
680}
7eda085c 681
b1f58330 682static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
8c3a5a44
KZ
683{
684 struct sigaction sa;
6dbe3af9 685
8c3a5a44 686 DBG(FRONTEND, dbgprint("ui: init"));
6dbe3af9 687
8c3a5a44
KZ
688 /* setup SIGCHLD handler */
689 sigemptyset(&sa.sa_mask);
690 sa.sa_flags = 0;
691 sa.sa_handler = die_on_signal;
692 sigaction(SIGINT, &sa, NULL);
693 sigaction(SIGTERM, &sa, NULL);
6dbe3af9 694
83fa0f80 695 ui_enabled = 1;
8c3a5a44 696 initscr();
6dbe3af9 697
1af8003b
KZ
698 if (has_colors()) {
699 size_t i;
700
701 start_color();
702 use_default_colors();
703
704 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
705 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
706 }
707
8c3a5a44
KZ
708 cbreak();
709 noecho();
710 nonl();
711 curs_set(0);
712 keypad(stdscr, TRUE);
6dbe3af9 713
8c3a5a44 714 return 0;
7eda085c
KZ
715}
716
8c3a5a44
KZ
717static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
718{
719 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
720 size_t items = COLS / len; /* items per line */
7eda085c 721
8c3a5a44 722 return MENU_START_LINE + ((idx / items));
7eda085c
KZ
723}
724
8c3a5a44
KZ
725static int menuitem_get_column(struct cfdisk *cf, size_t idx)
726{
727 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
728 size_t items = COLS / len; /* items per line */
729 size_t extra = items < cf->menu->nitems ? /* extra space on line */
730 COLS % len : /* - multi-line menu */
731 COLS - (cf->menu->nitems * len); /* - one line menu */
df1dddf9 732
8c3a5a44 733 extra += MENU_PADDING; /* add padding after last item to extra */
e66ac5d3 734
8c3a5a44
KZ
735 if (idx < items)
736 return (idx * len) + (extra / 2);
737 return ((idx % items) * len) + (extra / 2);
df1dddf9
KZ
738}
739
8c3a5a44
KZ
740static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx)
741{
742 struct cfdisk_menudesc *d;
743 size_t i;
7eda085c 744
8c3a5a44
KZ
745 for (i = 0, d = cf->menu->desc; d->name; d++) {
746 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
747 continue;
748 if (i++ == idx)
749 return d;
d26aa358 750 }
7eda085c 751
8c3a5a44 752 return NULL;
6dbe3af9
KZ
753}
754
83fa0f80
KZ
755static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf,
756 int key, size_t *idx)
757{
758 struct cfdisk_menudesc *d;
759
760 for (*idx = 0, d = cf->menu->desc; d->name; d++) {
761 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
762 continue;
763 if (key == d->key)
764 return d;
765 (*idx)++;
766 }
767
768 return NULL;
769}
770
8c3a5a44
KZ
771static void ui_draw_menuitem(struct cfdisk *cf,
772 struct cfdisk_menudesc *d,
773 size_t idx)
774{
775 char buf[80 * MB_CUR_MAX];
776 const char *name;
777 size_t width = cf->menu->width + 2; /* 2 = blank around string */
778 int ln, cl;
779
780 name = _(d->name);
781 mbsalign(name, buf, sizeof(buf), &width, MBS_ALIGN_CENTER, 0);
782
783 ln = menuitem_get_line(cf, idx);
784 cl = menuitem_get_column(cf, idx);
785
786 DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
787 cl, ln, buf));
788
789 if (cf->menu_idx == idx) {
790 standout();
791 mvprintw(ln, cl, "[%s]", buf);
792 standend();
793 if (d->desc)
b1f58330 794 ui_hint(d->desc);
8c3a5a44
KZ
795 } else
796 mvprintw(ln, cl, "[%s]", buf);
6dbe3af9
KZ
797}
798
8c3a5a44
KZ
799static void ui_draw_menu(struct cfdisk *cf)
800{
801 struct cfdisk_menudesc *d;
802 size_t i = 0;
7eda085c 803
8c3a5a44
KZ
804 assert(cf);
805 assert(cf->menu);
fd6b7a7f 806
8c3a5a44 807 DBG(FRONTEND, dbgprint("ui: menu: draw start"));
6dbe3af9 808
00b4f26a
KZ
809 for (i = MENU_START_LINE; i < (size_t) LINES - 1; i++) {
810 move(i, 0);
811 clrtoeol();
812 }
813
8c3a5a44 814 menu_update_ignore(cf);
6dbe3af9 815
00b4f26a 816 i = 0;
8c3a5a44
KZ
817 while ((d = menu_get_menuitem(cf, i)))
818 ui_draw_menuitem(cf, d, i++);
6dbe3af9 819
8c3a5a44 820 DBG(FRONTEND, dbgprint("ui: menu: draw end."));
6dbe3af9
KZ
821}
822
8c3a5a44
KZ
823static void ui_menu_goto(struct cfdisk *cf, int where)
824{
825 struct cfdisk_menudesc *d;
826 size_t old;
827
828 if (where < 0)
829 where = cf->menu->nitems - 1;
830 else if ((size_t) where > cf->menu->nitems - 1)
831 where = 0;
832 if ((size_t) where == cf->menu_idx)
833 return;
6dbe3af9 834
1af8003b
KZ
835 ui_clean_info();
836
8c3a5a44
KZ
837 old = cf->menu_idx;
838 cf->menu_idx = where;
6dbe3af9 839
8c3a5a44
KZ
840 d = menu_get_menuitem(cf, old);
841 ui_draw_menuitem(cf, d, old);
6dbe3af9 842
8c3a5a44
KZ
843 d = menu_get_menuitem(cf, where);
844 ui_draw_menuitem(cf, d, where);
6dbe3af9
KZ
845}
846
8460875d 847/* returns: error: < 0, success: 0, quit: 1 */
8c3a5a44
KZ
848static int ui_menu_action(struct cfdisk *cf, int key)
849{
8460875d
KZ
850 assert(cf);
851 assert(cf->menu);
852 assert(cf->menu->callback);
853
854 if (key == 0) {
855 struct cfdisk_menudesc *d = menu_get_menuitem(cf, cf->menu_idx);
856 if (!d)
857 return 0;
858 key = d->key;
00b4f26a
KZ
859
860 } else if (key != 'w' && key != 'W')
861 key = tolower(key); /* case insensitive except 'W'rite */
8460875d
KZ
862
863 DBG(FRONTEND, dbgprint("ui: menu action: key=%c", key));
864
865 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
866 DBG(FRONTEND, dbgprint(" ignore '%c'", key));
867 return 0;
868 }
869
870 return cf->menu->callback(cf, key);
6dbe3af9
KZ
871}
872
8c3a5a44
KZ
873static void ui_draw_partition(struct cfdisk *cf, size_t i)
874{
875 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
876 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
6dbe3af9 877
8c3a5a44 878 DBG(FRONTEND, dbgprint("ui: draw partition %zu", i));
6dbe3af9 879
8c3a5a44
KZ
880 if (cf->lines_idx == i) {
881 standout();
882 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
883 mvaddstr(ln, cl, cf->lines[i + 1]);
884 standend();
6dbe3af9 885 } else {
8c3a5a44
KZ
886 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
887 mvaddstr(ln, cl, cf->lines[i + 1]);
6dbe3af9
KZ
888 }
889
6dbe3af9
KZ
890}
891
8c3a5a44
KZ
892static int ui_draw_table(struct cfdisk *cf)
893{
894 int cl = ARROW_CURSOR_WIDTH;
895 size_t i, nparts = fdisk_table_get_nents(cf->table);
7eda085c 896
8c3a5a44 897 DBG(FRONTEND, dbgprint("ui: draw table"));
6dbe3af9 898
8c3a5a44
KZ
899 if (cf->nlines - 2 < cf->lines_idx)
900 cf->lines_idx = cf->nlines - 2; /* don't count header */
6dbe3af9 901
8c3a5a44
KZ
902 /* print header */
903 attron(A_BOLD);
904 mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
905 attroff(A_BOLD);
6dbe3af9 906
8c3a5a44
KZ
907 /* print partitions */
908 for (i = 0; i < nparts; i++)
909 ui_draw_partition(cf, i);
6dbe3af9 910
8c3a5a44 911 return 0;
6dbe3af9
KZ
912}
913
8c3a5a44
KZ
914static int ui_table_goto(struct cfdisk *cf, int where)
915{
916 size_t old;
917 size_t nparts = fdisk_table_get_nents(cf->table);
6dbe3af9 918
8c3a5a44 919 DBG(FRONTEND, dbgprint("ui: goto table %d", where));
6dbe3af9 920
8c3a5a44
KZ
921 if (where < 0)
922 where = 0;
923 else if ((size_t) where > nparts - 1)
924 where = nparts - 1;
6dbe3af9 925
8c3a5a44
KZ
926 if ((size_t) where == cf->lines_idx)
927 return 0;
6dbe3af9 928
8c3a5a44
KZ
929 old = cf->lines_idx;
930 cf->lines_idx = where;
6dbe3af9 931
8c3a5a44
KZ
932 ui_draw_partition(cf, old); /* cleanup old */
933 ui_draw_partition(cf, where); /* draw new */
1af8003b 934 ui_clean_info();
8c3a5a44
KZ
935 ui_draw_menu(cf);
936 refresh();
937 return 0;
6dbe3af9
KZ
938}
939
8c3a5a44
KZ
940static int ui_refresh(struct cfdisk *cf)
941{
942 char *id = NULL;
943 uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size;
944 char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
945 | SIZE_SUFFIX_3LETTER, bytes);
946 erase();
947
83fa0f80 948 if (!ui_enabled)
8c3a5a44
KZ
949 return -EINVAL;
950
951 /* header */
952 attron(A_BOLD);
1af8003b 953 ui_center(0, _("Disk: %s"), cf->cxt->dev_path);
8c3a5a44 954 attroff(A_BOLD);
1af8003b 955 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
8c3a5a44
KZ
956 strsz, bytes, (uintmax_t) cf->cxt->total_sectors);
957 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1af8003b 958 ui_center(2, _("Label: %s, identifier: %s"),
8c3a5a44 959 cf->cxt->label->name, id);
7eda085c 960 else
1af8003b 961 ui_center(2, _("Label: %s"));
8c3a5a44 962 free(strsz);
6dbe3af9 963
8c3a5a44
KZ
964 ui_draw_table(cf);
965 ui_draw_menu(cf);
6dbe3af9 966 refresh();
8c3a5a44 967 return 0;
6dbe3af9
KZ
968}
969
b1f58330
KZ
970static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
971 const char *hint, char *buf, size_t len)
972{
973 size_t cells = 0;
974 ssize_t i = 0, rc = -1;
975 wint_t c;
976 int ln = MENU_START_LINE, cl = 1;
977
978 assert(cf);
979 assert(buf);
980 assert(len);
981
982 move(ln, 0);
983 clrtoeol();
984
985 if (prompt) {
986 mvaddstr(ln, cl, prompt);
987 cl += mbs_safe_width(prompt);
988 }
989
990 /* default value */
991 if (*buf) {
992 i = strlen(buf);
993 cells = mbs_safe_width(buf);
994 mvaddstr(ln, cl, buf);
995 }
996
997 if (hint)
998 ui_hint(hint);
999 else
1000 ui_clean_hint();
1001
1002 move(ln, cl + cells);
1003 curs_set(1);
1004 refresh();
1005
1006 while (1) {
1007#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1008 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1009 if (get_wch(&c) == ERR) {
1010#else
1011 if ((c = getch()) == ERR) {
1012#endif
1013 if (!isatty(STDIN_FILENO))
1014 exit(2);
1015 else
1016 goto done;
1017 }
1018 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1019 break;
1020
1021 switch (c) {
1022 case KEY_ESC:
1023 rc = -CFDISK_ERR_ESC;
1024 goto done;
1025 case KEY_DELETE:
1026 case '\b':
1027 case KEY_BACKSPACE:
1028 if (i > 0) {
1029 cells--;
1030 i = mbs_truncate(buf, &cells);
1031 if (i < 0)
1032 goto done;
1033 mvaddch(ln, cl + cells, ' ');
1034 move(ln, cl + cells);
1035 } else
1036 beep();
1037 break;
1038 default:
1039#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1040 if (i + 1 < (ssize_t) len && iswprint(c)) {
1041 wchar_t wc = (wchar_t) c;
1042 char s[MB_CUR_MAX + 1];
1043 int sz = wctomb(s, wc);
1044
1045 if (sz > 0 && sz + i < (ssize_t) len) {
1046 s[sz] = '\0';
1047 mvaddnstr(ln, cl + cells, s, sz);
1048 memcpy(buf + i, s, sz);
1049 i += sz;
1050 buf[i] = '\0';
1051 cells += wcwidth(wc);
1052 } else
1053 beep();
1054 }
1055#else
1056 if (i + 1 < (ssize_t) len && isprint(c)) {
1057 mvaddch(ln, cl + cells, c);
1058 str[i++] = c;
1059 str[i] = '\0';
1060 cells++;
1061 }
1062#endif
1063 else
1064 beep();
1065 }
1066 refresh();
1067 }
1068
1069 rc = i; /* success */
1070done:
1071 move(ln, 0);
1072 clrtoeol();
1073 curs_set(0);
1074 refresh();
1075
1076 return rc;
1077}
1078
1079/* @res is default value as well as result in bytes */
1080static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
1081 uintmax_t low, uintmax_t up)
1082{
1083 char buf[128];
1084 uintmax_t user = 0;
1085 ssize_t rc;
1086 char *dflt = size_to_human_string(0, *res);
1087
1088 DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res));
1089
1090 ui_clean_info();
1091
1092 do {
1093 int pwr = 0;
1094
1095 snprintf(buf, sizeof(buf), "%s", dflt);
1096 rc = ui_get_string(cf, prompt,
1097 _("The size may be followed by MiB, GiB or TiB suffix "
1098 "(the \"iB\" is optional)."),
1099 buf, sizeof(buf));
1100 if (rc == 0) {
1101 ui_warnx(_("Please, specify size."));
1102 continue; /* nothing specified */
1103 } else if (rc == -CFDISK_ERR_ESC)
1104 break; /* cancel dialog */
1105
1106 rc = parse_size(buf, &user, &pwr); /* response, parse */
1107 if (rc == 0) {
1108 DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d", user, pwr));
1109 if (user < low) {
1110 ui_warnx(_("Minimal size is %ju"), low);
1111 rc = -ERANGE;
1112 }
1113 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1114 /* ignore when the user specified size overflow
1115 * with in range specified by suffix (e.g. MiB) */
1116 user = up;
1117
1118 if (user > up) {
1119 ui_warnx(_("Maximal size is %ju bytes."), up);
1120 rc = -ERANGE;
1121 }
1122 }
1123 } while (rc != 0);
1124
1125 if (rc == 0)
1126 *res = user;
1127 free(dflt);
1128
1129 DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc));
1130 return rc;
1131}
1132
1133
8c3a5a44
KZ
1134static int ui_run(struct cfdisk *cf)
1135{
1136 int rc;
6dbe3af9 1137
8c3a5a44 1138 DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES));
6dbe3af9 1139
b1f58330 1140 menu_push(cf, CFDISK_MENU_MAIN, NULL);
6dbe3af9 1141
8c3a5a44
KZ
1142 rc = ui_refresh(cf);
1143 if (rc)
1144 return rc;
6dbe3af9 1145
8c3a5a44 1146 do {
8460875d 1147 int rc = 0, key = getch();
6dbe3af9 1148
8c3a5a44
KZ
1149 switch (key) {
1150 case KEY_DOWN:
1151 case '\016': /* ^N */
1152 case 'j': /* Vi-like alternative */
1153 ui_table_goto(cf, cf->lines_idx + 1);
1154 break;
1155 case KEY_UP:
1156 case '\020': /* ^P */
1157 case 'k': /* Vi-like alternative */
1158 ui_table_goto(cf, cf->lines_idx - 1);
1159 break;
1160 case KEY_HOME:
1161 ui_table_goto(cf, 0);
1162 break;
1163 case KEY_END:
1164 ui_table_goto(cf, cf->nlines - 1);
1165 break;
1166 ui_menu_action(cf, 0);
1167 break;
1168 case KEY_LEFT:
1169#ifdef KEY_BTAB
1170 case KEY_BTAB:
6dbe3af9 1171#endif
8c3a5a44
KZ
1172 ui_menu_goto(cf, cf->menu_idx - 1);
1173 break;
1174 case KEY_RIGHT:
1175 case '\t':
1176 ui_menu_goto(cf, cf->menu_idx + 1);
1177 break;
1178 case KEY_ENTER:
1179 case '\n':
1180 case '\r':
8460875d 1181 rc = ui_menu_action(cf, 0);
8c3a5a44
KZ
1182 break;
1183 default:
8460875d
KZ
1184 rc = ui_menu_action(cf, key);
1185 if (rc < 0)
8c3a5a44
KZ
1186 beep();
1187 break;
1188 }
8460875d
KZ
1189
1190 if (rc == 1)
1191 break; /* quit */
8c3a5a44 1192 } while (1);
6dbe3af9 1193
8c3a5a44 1194 menu_pop(cf);
6dbe3af9 1195
8c3a5a44
KZ
1196 DBG(FRONTEND, dbgprint("ui: end"));
1197
1198 return 0;
1199}
6dbe3af9 1200
8c3a5a44
KZ
1201int main(int argc, char *argv[])
1202{
1203 struct cfdisk _cf = { .lines_idx = 0 },
1204 *cf = &_cf;
6dbe3af9 1205
8c3a5a44
KZ
1206 setlocale(LC_ALL, "");
1207 bindtextdomain(PACKAGE, LOCALEDIR);
1208 textdomain(PACKAGE);
1209 atexit(close_stdout);
6dbe3af9 1210
8c3a5a44
KZ
1211 fdisk_init_debug(0);
1212 cf->cxt = fdisk_new_context();
1213 if (!cf->cxt)
1214 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
6dbe3af9 1215
8c3a5a44
KZ
1216 fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf);
1217 fdisk_context_enable_freespace(cf->cxt, 1);
6dbe3af9 1218
8c3a5a44
KZ
1219 if (argc != 2)
1220 err(EXIT_FAILURE, "usage: %s <device>", argv[0]);
6dbe3af9 1221
8c3a5a44
KZ
1222 if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0)
1223 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
6dbe3af9 1224
8c3a5a44 1225 cols_init(cf);
fd6b7a7f 1226
8460875d 1227 if (lines_refresh(cf))
8c3a5a44 1228 errx(EXIT_FAILURE, _("failed to read partitions"));
6dbe3af9 1229
8c3a5a44
KZ
1230 /* Don't use err(), warn() from this point */
1231 ui_init(cf);
1232 ui_run(cf);
1233 ui_end(cf);
6dbe3af9 1234
8c3a5a44
KZ
1235 free(cf->lines);
1236 free(cf->linesbuf);
1237 fdisk_unref_table(cf->table);
1238 fdisk_free_context(cf->cxt);
1239 return EXIT_SUCCESS;
6dbe3af9 1240}