]> git.ipfire.org Git - thirdparty/util-linux.git/blame - fdisks/cfdisk.c
cfdisk: menu actions refactoring
[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,
45333e9d
KZ
60 CFDISK_CL_WARNING,
61 CFDISK_CL_FREESPACE,
1af8003b
KZ
62};
63static const int color_pairs[][2] = {
64 /* color foreground, background */
45333e9d
KZ
65 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
66 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 }
1af8003b 67};
8c3a5a44 68
8460875d 69struct cfdisk;
8460875d 70
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 80static void ui_draw_menu(struct cfdisk *cf);
ac27ea5c
KZ
81static int ui_menu_move(struct cfdisk *cf, int key);
82
b1f58330
KZ
83static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
84 uintmax_t low, uintmax_t up);
8460875d 85
83fa0f80 86static int ui_enabled;
8c3a5a44
KZ
87
88struct cfdisk_menudesc {
89 int key; /* keyboard shortcut */
90 const char *name; /* item name */
91 const char *desc; /* item description */
749af4b6
KZ
92};
93
8c3a5a44 94struct cfdisk_menu {
8a726114 95 char *title;
8c3a5a44
KZ
96 struct cfdisk_menudesc *desc;
97 char *ignore;
98 size_t id;
99 size_t width;
100 size_t nitems;
8460875d 101
8c3a5a44 102 struct cfdisk_menu *prev;
8a726114
KZ
103
104 unsigned int vertical : 1;
749af4b6
KZ
105};
106
8c3a5a44
KZ
107static struct cfdisk_menudesc menu_main[] = {
108 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
109 { 'd', N_("Delete"), N_("Delete the current partition") },
110// { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
111// { 'h', N_("Help"), N_("Print help screen") },
112// { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
113 { 'n', N_("New"), N_("Create new partition from free space") },
114// { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
115 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
116 { 't', N_("Type"), N_("Change the partition type") },
117// { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
118 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
119 { 0, NULL, NULL }
120};
6dbe3af9 121
8c3a5a44 122enum {
b1f58330
KZ
123 CFDISK_MENU_GENERATED = -1, /* used in libfdisk callback */
124
125 /* built-in menus */
8c3a5a44 126 CFDISK_MENU_MAIN = 0,
b1f58330 127
5c36a0eb
KZ
128};
129
8c3a5a44
KZ
130static struct cfdisk_menudesc *menus[] = {
131 [CFDISK_MENU_MAIN] = menu_main
132};
7eda085c 133
8c3a5a44
KZ
134struct cfdisk {
135 struct fdisk_context *cxt; /* libfdisk context */
136 struct fdisk_table *table; /* partition table */
7eda085c 137
8c3a5a44
KZ
138 struct cfdisk_menu *menu; /* the current menu */
139 size_t menu_idx;
7eda085c 140
8c3a5a44
KZ
141 int *cols; /* output columns */
142 size_t ncols; /* number of columns */
7eda085c 143
8c3a5a44
KZ
144 char *linesbuf; /* table as string */
145 size_t linesbufsz; /* size of the tb_buf */
7eda085c 146
8c3a5a44
KZ
147 char **lines; /* array with lines */
148 size_t nlines; /* number of lines */
149 size_t lines_idx; /* current line <0..N>, exclude header */
8c3a5a44 150};
5c36a0eb 151
8c3a5a44
KZ
152static int cols_init(struct cfdisk *cf)
153{
154 assert(cf);
5c36a0eb 155
8c3a5a44
KZ
156 free(cf->cols);
157 cf->cols = NULL;
158 cf->ncols = 0;
5c36a0eb 159
8c3a5a44 160 return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols);
5c36a0eb
KZ
161}
162
8c3a5a44
KZ
163/* It would be possible to use fdisk_table_to_string(), but we want some
164 * extension to the output format, so let's do it without libfdisk
165 */
166static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
167{
8c3a5a44 168 const struct fdisk_column *col;
8903f7df 169 struct fdisk_partition *pa;
8c3a5a44
KZ
170 struct fdisk_label *lb;
171 struct fdisk_iter *itr = NULL;
172 struct tt *tt = NULL;
173 char *res = NULL;
8903f7df 174 size_t i;
45333e9d 175 int tree = 0;
d051ea93 176 struct tt_line *ln, *ln_cont = NULL;
8c3a5a44
KZ
177
178 DBG(FRONTEND, dbgprint("table: convert to string"));
179
180 assert(cf);
181 assert(cf->cxt);
182 assert(cf->cols);
183 assert(tb);
184
185 lb = fdisk_context_get_label(cf->cxt, NULL);
186 assert(lb);
187
8c3a5a44
KZ
188 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
189 if (!itr)
190 goto done;
5c36a0eb 191
45333e9d
KZ
192 /* get container (e.g. extended partition) */
193 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
194 if (fdisk_partition_is_nested(pa)) {
195 DBG(FRONTEND, dbgprint("table: nested detected, using tree"));
45333e9d
KZ
196 tree = TT_FL_TREE;
197 break;
198 }
199 }
45333e9d
KZ
200
201 tt = tt_new_table(TT_FL_FREEDATA | TT_FL_MAX | tree);
202 if (!tt)
203 goto done;
204
8c3a5a44
KZ
205 /* headers */
206 for (i = 0; i < cf->ncols; i++) {
207 col = fdisk_label_get_column(lb, cf->cols[i]);
45333e9d
KZ
208 if (col) {
209 int fl = col->tt_flags;
45333e9d
KZ
210 if (tree && col->id == FDISK_COL_DEVICE)
211 fl |= TT_FL_TREE;
212 tt_define_column(tt, col->name, col->width, fl);
213 }
8c3a5a44
KZ
214 }
215
216 /* data */
8903f7df
KZ
217 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
218
8c3a5a44 219 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
8903f7df
KZ
220 struct tt_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
221
222 ln = tt_add_line(tt, parent);
8c3a5a44
KZ
223 if (!ln)
224 goto done;
225 for (i = 0; i < cf->ncols; i++) {
226 char *cdata = NULL;
8c3a5a44
KZ
227 col = fdisk_label_get_column(lb, cf->cols[i]);
228 if (!col)
229 continue;
230 if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata))
231 continue;
232 tt_line_set_data(ln, i, cdata);
233 }
8903f7df 234 if (tree && fdisk_partition_is_container(pa))
45333e9d 235 ln_cont = ln;
d051ea93
KZ
236
237 tt_line_set_userdata(ln, (void *) pa);
238 fdisk_ref_partition(pa);
8c3a5a44 239 }
6dbe3af9 240
d051ea93
KZ
241 if (tt_is_empty(tt))
242 goto done;
243
244 tt_set_termreduce(tt, ARROW_CURSOR_WIDTH);
245 tt_print_table_to_string(tt, &res);
246
247 /* tt_* code might to reorder lines, let's reorder @tb according to the
248 * final output (it's no problem because partitions are addressed by
249 * parno stored within struct fdisk_partition) */
250
251 /* remove all */
252 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
253 while (fdisk_table_next_partition(tb, itr, &pa) == 0)
254 fdisk_table_remove_partition(tb, pa);
255
256 /* add all in the right order */
257 i = 0;
258 while (tt_get_output_line(tt, i++, &ln) == 0) {
259 struct fdisk_partition *pa = tt_line_get_userdata(ln);
260
261 fdisk_table_add_partition(tb, pa);
262 fdisk_unref_partition(pa);
8c3a5a44
KZ
263 }
264done:
265 tt_free_table(tt);
266 fdisk_free_iter(itr);
6dbe3af9 267
8c3a5a44 268 return res;
2b6fc908
KZ
269}
270
8460875d 271static int lines_refresh(struct cfdisk *cf)
8c3a5a44
KZ
272{
273 int rc;
274 char *p;
275 size_t i;
7eda085c 276
8c3a5a44 277 assert(cf);
2b6fc908 278
1af8003b 279 DBG(FRONTEND, dbgprint("refreshing buffer"));
c64061c9 280
8c3a5a44
KZ
281 free(cf->linesbuf);
282 free(cf->lines);
283 cf->linesbuf = NULL;
284 cf->linesbufsz = 0;
285 cf->lines = NULL;
286 cf->nlines = 0;
6dbe3af9 287
8c3a5a44 288 fdisk_unref_table(cf->table);
1af8003b 289 cf->table = NULL;
6dbe3af9 290
2cec7949
KZ
291 /* read partitions and free spaces into cf->table */
292 rc = fdisk_get_partitions(cf->cxt, &cf->table);
293 if (!rc)
294 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
8c3a5a44
KZ
295 if (rc)
296 return rc;
0d8589c5 297
8c3a5a44
KZ
298 cf->linesbuf = table_to_string(cf, cf->table);
299 if (!cf->linesbuf)
300 return -ENOMEM;
6dbe3af9 301
8c3a5a44
KZ
302 cf->linesbufsz = strlen(cf->linesbuf);
303 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
6dbe3af9 304
8c3a5a44
KZ
305 cf->lines = calloc(cf->nlines, sizeof(char *));
306 if (!cf->lines)
307 return -ENOMEM;
6dbe3af9 308
8c3a5a44
KZ
309 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
310 cf->lines[i] = p;
311 p = strchr(p, '\n');
312 if (p) {
313 *p = '\0';
314 p++;
315 }
316 }
6dbe3af9 317
8c3a5a44 318 return 0;
6dbe3af9
KZ
319}
320
00b4f26a
KZ
321static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
322{
323 assert(cf);
324 assert(cf->table);
325
326 return fdisk_table_get_partition(cf->table, cf->lines_idx);
327}
328
45333e9d
KZ
329static int is_freespace(struct cfdisk *cf, size_t i)
330{
331 struct fdisk_partition *pa;
332
333 assert(cf);
334 assert(cf->table);
335
336 pa = fdisk_table_get_partition(cf->table, i);
337 return fdisk_partition_is_freespace(pa);
338}
339
b1f58330
KZ
340/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
341 * responseback to libfdisk
342 */
343static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
344{
345 struct cfdisk_menudesc *d, *cm;
346 int key;
347 size_t i = 0, nitems;
348 const char *name, *desc;
349
350 assert(ask);
351 assert(cf);
352
353 /* create cfdisk menu according to libfdisk ask-menu, note that the
354 * last cm[] item has to be empty -- so nitems + 1 */
355 nitems = fdisk_ask_menu_get_nitems(ask);
356 cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
357 if (!cm)
358 return -ENOMEM;
359
360 for (i = 0; i < nitems; i++) {
361 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
362 break;
363 cm[i].key = key;
364 cm[i].desc = desc;
365 cm[i].name = name;
366 }
367
368 /* make the new menu active */
369 menu_push(cf, CFDISK_MENU_GENERATED, cm);
370 ui_draw_menu(cf);
371 refresh();
372
373 /* wait for keys */
374 do {
ac27ea5c
KZ
375 int key = getch();
376
377 if (ui_menu_move(cf, key) == 0)
378 continue;
379
380 switch (key) {
b1f58330
KZ
381 case KEY_ENTER:
382 case '\n':
383 case '\r':
384 d = menu_get_menuitem(cf, cf->menu_idx);
385 if (d)
386 fdisk_ask_menu_set_result(ask, d->key);
387 menu_pop(cf);
388 free(cm);
389 return 0;
390 }
391 } while (1);
392
393 menu_pop(cf);
394 free(cm);
395 return -1;
396}
397
398
8c3a5a44
KZ
399static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
400 void *data __attribute__((__unused__)))
401{
402 int rc = 0;
6dbe3af9 403
8c3a5a44
KZ
404 assert(cxt);
405 assert(ask);
6dbe3af9 406
8c3a5a44
KZ
407 switch(fdisk_ask_get_type(ask)) {
408 case FDISK_ASKTYPE_INFO:
83fa0f80 409 ui_info(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
410 break;
411 case FDISK_ASKTYPE_WARNX:
83fa0f80 412 ui_warnx(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
413 break;
414 case FDISK_ASKTYPE_WARN:
83fa0f80 415 ui_warn(fdisk_ask_print_get_mesg(ask));
8c3a5a44 416 break;
b1f58330
KZ
417 case FDISK_ASKTYPE_MENU:
418 ask_menu(ask, (struct cfdisk *) data);
419 break;
8c3a5a44 420 default:
83fa0f80
KZ
421 ui_warnx(_("internal error: unsupported dialog type %d"),
422 fdisk_ask_get_type(ask));
8c3a5a44
KZ
423 return -EINVAL;
424 }
425 return rc;
fd6b7a7f 426}
6dbe3af9 427
7aa0d529 428static int ui_end(void)
8c3a5a44 429{
7aa0d529 430 if (!ui_enabled)
8c3a5a44
KZ
431 return -EINVAL;
432
48d7b13a 433#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
8c3a5a44
KZ
434 SLsmg_gotorc(LINES - 1, 0);
435 SLsmg_refresh();
2b6fc908 436#else
8c3a5a44 437 mvcur(0, COLS - 1, LINES-1, 0);
2b6fc908 438#endif
8c3a5a44
KZ
439 nl();
440 endwin();
441 printf("\n");
7aa0d529 442 ui_enabled = 0;
8c3a5a44 443 return 0;
6dbe3af9
KZ
444}
445
1af8003b 446static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap)
f609e92e 447{
8c3a5a44 448 size_t width;
8c3a5a44 449 char *buf = NULL;
f609e92e 450
8c3a5a44
KZ
451 move(line, 0);
452 clrtoeol();
f609e92e 453
8c3a5a44 454 xvasprintf(&buf, fmt, ap);
fd6b7a7f 455
b1f58330 456 width = mbs_safe_width(buf);
91ba41ca
KZ
457 if (width > (size_t) COLS) {
458 char *p = strrchr(buf + COLS, ' ');
459 if (!p)
460 p = buf + COLS;
461 *p = '\0';
462 if (line + 1 >= LINES)
463 line--;
464 attron(attrs);
465 mvaddstr(line, 0, buf);
466 mvaddstr(line + 1, 0, p+1);
467 attroff(attrs);
468 } else {
469 attron(attrs);
470 mvaddstr(line, (COLS - width) / 2, buf);
471 attroff(attrs);
472 }
8c3a5a44 473 free(buf);
6dbe3af9
KZ
474}
475
1af8003b
KZ
476static void ui_center(int line, const char *fmt, ...)
477{
478 va_list ap;
479 va_start(ap, fmt);
480 ui_vprint_center(line, 0, fmt, ap);
481 va_end(ap);
482}
483
83fa0f80
KZ
484static void ui_warnx(const char *fmt, ...)
485{
486 va_list ap;
487 va_start(ap, fmt);
488 if (ui_enabled)
489 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt, ap);
490 else
491 vfprintf(stderr, fmt, ap);
492 va_end(ap);
493}
494
495static void ui_warn(const char *fmt, ...)
1af8003b 496{
83fa0f80 497 char *fmt_m;
1af8003b 498 va_list ap;
83fa0f80
KZ
499
500 xasprintf(&fmt_m, "%s: %m", fmt);
501
1af8003b 502 va_start(ap, fmt);
83fa0f80
KZ
503 if (ui_enabled)
504 ui_vprint_center(INFO_LINE, COLOR_PAIR(CFDISK_CL_WARNING), fmt_m, ap);
505 else
506 vfprintf(stderr, fmt_m, ap);
1af8003b 507 va_end(ap);
83fa0f80 508 free(fmt_m);
1af8003b
KZ
509}
510
7aa0d529
KZ
511static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
512 {
513 va_list ap;
514 ui_end();
515
516 va_start(ap, fmt);
517 fprintf(stderr, "%s: ", program_invocation_short_name);
518 vfprintf(stderr, fmt, ap);
519 va_end(ap);
520
521 exit(rc);
522}
523
1af8003b
KZ
524static void ui_info(const char *fmt, ...)
525{
526 va_list ap;
527 va_start(ap, fmt);
83fa0f80
KZ
528 if (ui_enabled)
529 ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap);
530 else
531 vfprintf(stdout, fmt, ap);
1af8003b
KZ
532 va_end(ap);
533}
534
535static void ui_clean_info(void)
536{
537 move(INFO_LINE, 0);
538 clrtoeol();
539}
6dbe3af9 540
b1f58330
KZ
541static void ui_hint(const char *fmt, ...)
542{
543 va_list ap;
544 va_start(ap, fmt);
545 if (ui_enabled)
546 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
547 else
548 vfprintf(stdout, fmt, ap);
549 va_end(ap);
550}
551
552static void ui_clean_hint(void)
553{
554 move(HINT_LINE, 0);
555 clrtoeol();
556}
557
8c3a5a44
KZ
558static void die_on_signal(int dummy __attribute__((__unused__)))
559{
7aa0d529
KZ
560 DBG(FRONTEND, dbgprint("die on signal."));
561 ui_end();
8c3a5a44 562 exit(EXIT_FAILURE);
6dbe3af9
KZ
563}
564
8c3a5a44
KZ
565static void menu_update_ignore(struct cfdisk *cf)
566{
00b4f26a
KZ
567 char ignore[128] = { 0 };
568 int i = 0;
569 struct fdisk_partition *pa;
8c3a5a44 570 struct cfdisk_menu *m;
83fa0f80
KZ
571 struct cfdisk_menudesc *d, *org;
572 size_t idx;
6dbe3af9 573
8c3a5a44 574 assert(cf);
6dbe3af9 575
8c3a5a44 576 m = cf->menu;
83fa0f80
KZ
577 org = menu_get_menuitem(cf, cf->menu_idx);
578
8c3a5a44 579 DBG(FRONTEND, dbgprint("menu: update menu ignored keys"));
6dbe3af9 580
8c3a5a44
KZ
581 switch (m->id) {
582 case CFDISK_MENU_MAIN:
00b4f26a
KZ
583 pa = get_current_partition(cf);
584 if (!pa)
585 break;
586 if (fdisk_partition_is_freespace(pa)) {
587 ignore[i++] = 'd'; /* delete */
588 ignore[i++] = 't'; /* set type */
589 ignore[i++] = 'b'; /* set bootable */
83fa0f80 590 } else {
00b4f26a 591 ignore[i++] = 'n';
83fa0f80
KZ
592 if (!fdisk_is_disklabel(cf->cxt, DOS) &&
593 !fdisk_is_disklabel(cf->cxt, SGI))
594 ignore[i++] = 'b';
595 }
00b4f26a 596
8c3a5a44 597 break;
fd6b7a7f 598 }
6dbe3af9 599
00b4f26a
KZ
600 ignore[i] = '\0';
601
8c3a5a44 602 /* return if no change */
00b4f26a
KZ
603 if ( (!m->ignore && !*ignore)
604 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
8c3a5a44 605 return;
fd6b7a7f 606 }
6dbe3af9 607
8c3a5a44 608 free(m->ignore);
8460875d 609 m->ignore = xstrdup(ignore);
8c3a5a44 610 m->nitems = 0;
6dbe3af9 611
8c3a5a44
KZ
612 for (d = m->desc; d->name; d++) {
613 if (m->ignore && strchr(m->ignore, d->key))
8460875d
KZ
614 continue;
615 m->nitems++;
8c3a5a44 616 }
83fa0f80
KZ
617
618 /* refresh menu index to be at the same menuitem or go to the first */
619 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
620 cf->menu_idx = idx;
621 else
622 cf->menu_idx = 0;
6dbe3af9
KZ
623}
624
b1f58330
KZ
625static struct cfdisk_menu *menu_push(
626 struct cfdisk *cf,
627 size_t id,
628 struct cfdisk_menudesc *desc)
8c3a5a44
KZ
629{
630 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
631 struct cfdisk_menudesc *d;
6dbe3af9 632
8c3a5a44 633 assert(cf);
6dbe3af9 634
8c3a5a44 635 DBG(FRONTEND, dbgprint("menu: new menu"));
6dbe3af9 636
8c3a5a44
KZ
637 m->prev = cf->menu;
638 m->id = id;
b1f58330 639 m->desc = desc ? desc : menus[id];
6dbe3af9 640
8c3a5a44
KZ
641 for (d = m->desc; d->name; d++) {
642 const char *name = _(d->name);
b1f58330 643 size_t len = mbs_safe_width(name);
8c3a5a44
KZ
644 if (len > m->width)
645 m->width = len;
646 m->nitems++;
647 }
6dbe3af9 648
8c3a5a44
KZ
649 cf->menu = m;
650 return m;
6dbe3af9
KZ
651}
652
8c3a5a44 653static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
6dbe3af9 654{
8c3a5a44 655 struct cfdisk_menu *m = NULL;
6dbe3af9 656
8c3a5a44 657 assert(cf);
7eda085c 658
8c3a5a44 659 DBG(FRONTEND, dbgprint("menu: rem menu"));
7eda085c 660
8c3a5a44
KZ
661 if (cf->menu) {
662 m = cf->menu->prev;
663 free(cf->menu->ignore);
8a726114 664 free(cf->menu->title);
8c3a5a44 665 free(cf->menu);
c07ebfa1 666 }
8c3a5a44
KZ
667 cf->menu = m;
668 return cf->menu;
6dbe3af9
KZ
669}
670
8a726114
KZ
671static void menu_set_title(struct cfdisk_menu *m, const char *title)
672{
673 char *str = NULL;
674
675 if (title) {
676 size_t len = mbs_safe_width(title);
677 if (len + 3 > m->width)
678 m->width = len + 3;
679 str = xstrdup(title);
680 }
681 m->title = str;
682}
683
684
b1f58330 685static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
8c3a5a44
KZ
686{
687 struct sigaction sa;
6dbe3af9 688
8c3a5a44 689 DBG(FRONTEND, dbgprint("ui: init"));
6dbe3af9 690
8c3a5a44
KZ
691 /* setup SIGCHLD handler */
692 sigemptyset(&sa.sa_mask);
693 sa.sa_flags = 0;
694 sa.sa_handler = die_on_signal;
695 sigaction(SIGINT, &sa, NULL);
696 sigaction(SIGTERM, &sa, NULL);
6dbe3af9 697
83fa0f80 698 ui_enabled = 1;
8c3a5a44 699 initscr();
6dbe3af9 700
1af8003b
KZ
701 if (has_colors()) {
702 size_t i;
703
704 start_color();
705 use_default_colors();
706
707 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
708 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
709 }
710
8c3a5a44
KZ
711 cbreak();
712 noecho();
713 nonl();
714 curs_set(0);
715 keypad(stdscr, TRUE);
6dbe3af9 716
8c3a5a44 717 return 0;
7eda085c
KZ
718}
719
8c3a5a44
KZ
720static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
721{
8a726114 722 if (cf->menu->vertical) {
7aa0d529
KZ
723 size_t ni = cf->menu->nitems + 1;
724 if ((size_t) LINES < ni)
725 return 0;
726 return (LINES - ni) / 2 + idx;
8a726114
KZ
727 } else {
728 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
729 size_t items = COLS / len; /* items per line */
730
731 return MENU_START_LINE + ((idx / items));
732 }
7eda085c
KZ
733}
734
8c3a5a44
KZ
735static int menuitem_get_column(struct cfdisk *cf, size_t idx)
736{
8a726114
KZ
737 if (cf->menu->vertical) {
738 size_t nc = cf->menu->width + MENU_PADDING;
7aa0d529
KZ
739 if ((size_t) COLS <= nc)
740 return 0;
741 return (COLS - nc) / 2;
8a726114
KZ
742 } else {
743 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
744 size_t items = COLS / len; /* items per line */
745 size_t extra = items < cf->menu->nitems ? /* extra space on line */
746 COLS % len : /* - multi-line menu */
747 COLS - (cf->menu->nitems * len); /* - one line menu */
748
749 extra += MENU_PADDING; /* add padding after last item to extra */
e66ac5d3 750
8a726114
KZ
751 if (idx < items)
752 return (idx * len) + (extra / 2);
753 return ((idx % items) * len) + (extra / 2);
754 }
df1dddf9
KZ
755}
756
8c3a5a44
KZ
757static struct cfdisk_menudesc *menu_get_menuitem(struct cfdisk *cf, size_t idx)
758{
759 struct cfdisk_menudesc *d;
760 size_t i;
7eda085c 761
8c3a5a44
KZ
762 for (i = 0, d = cf->menu->desc; d->name; d++) {
763 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
764 continue;
765 if (i++ == idx)
766 return d;
d26aa358 767 }
7eda085c 768
8c3a5a44 769 return NULL;
6dbe3af9
KZ
770}
771
83fa0f80
KZ
772static struct cfdisk_menudesc *menu_get_menuitem_by_key(struct cfdisk *cf,
773 int key, size_t *idx)
774{
775 struct cfdisk_menudesc *d;
776
777 for (*idx = 0, d = cf->menu->desc; d->name; d++) {
778 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
779 continue;
780 if (key == d->key)
781 return d;
782 (*idx)++;
783 }
784
785 return NULL;
786}
787
8c3a5a44
KZ
788static void ui_draw_menuitem(struct cfdisk *cf,
789 struct cfdisk_menudesc *d,
790 size_t idx)
791{
792 char buf[80 * MB_CUR_MAX];
793 const char *name;
794 size_t width = cf->menu->width + 2; /* 2 = blank around string */
8a726114 795 int ln, cl, vert = cf->menu->vertical;
8c3a5a44
KZ
796
797 name = _(d->name);
8a726114
KZ
798 mbsalign(name, buf, sizeof(buf), &width,
799 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
800 0);
8c3a5a44
KZ
801
802 ln = menuitem_get_line(cf, idx);
803 cl = menuitem_get_column(cf, idx);
804
805 DBG(FRONTEND, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
806 cl, ln, buf));
807
8a726114
KZ
808 if (vert) {
809 mvaddch(ln, cl - 1, ACS_VLINE);
810 mvaddch(ln, cl + cf->menu->width + 4, ACS_VLINE);
811 }
812
8c3a5a44
KZ
813 if (cf->menu_idx == idx) {
814 standout();
8a726114 815 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
8c3a5a44
KZ
816 standend();
817 if (d->desc)
b1f58330 818 ui_hint(d->desc);
8c3a5a44 819 } else
8a726114 820 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
6dbe3af9
KZ
821}
822
8c3a5a44
KZ
823static void ui_draw_menu(struct cfdisk *cf)
824{
825 struct cfdisk_menudesc *d;
8a726114 826 struct cfdisk_menu *m;
7aa0d529 827 size_t i = 0, maxi;
7eda085c 828
8c3a5a44
KZ
829 assert(cf);
830 assert(cf->menu);
fd6b7a7f 831
8c3a5a44 832 DBG(FRONTEND, dbgprint("ui: menu: draw start"));
6dbe3af9 833
8a726114 834 m = cf->menu;
7aa0d529 835 maxi = menuitem_get_line(cf, m->nitems);
8a726114 836
7aa0d529 837 for (i = menuitem_get_line(cf, 0); i <= maxi; i++) {
00b4f26a
KZ
838 move(i, 0);
839 clrtoeol();
840 }
841
8c3a5a44 842 menu_update_ignore(cf);
6dbe3af9 843
00b4f26a 844 i = 0;
8c3a5a44
KZ
845 while ((d = menu_get_menuitem(cf, i)))
846 ui_draw_menuitem(cf, d, i++);
6dbe3af9 847
8a726114
KZ
848 if (m->vertical) {
849 size_t ln = menuitem_get_line(cf, 0);
850 size_t cl = menuitem_get_column(cf, 0);
851 size_t nlines = i;
852
853 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
854 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
855
856 for (i = 0; i < m->width + 4; i++) {
857 mvaddch(ln - 1, cl + i, ACS_HLINE);
858 mvaddch(ln + nlines, cl + i, ACS_HLINE);
859 }
860 mvaddch(ln - 1, cl + i, ACS_URCORNER);
861 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
862
863 if (m->title) {
864 attron(A_BOLD);
865 mvprintw(ln - 1, cl, " %s ", m->title);
866 attroff(A_BOLD);
867 }
868
869 }
870
8c3a5a44 871 DBG(FRONTEND, dbgprint("ui: menu: draw end."));
6dbe3af9
KZ
872}
873
8c3a5a44
KZ
874static void ui_menu_goto(struct cfdisk *cf, int where)
875{
876 struct cfdisk_menudesc *d;
877 size_t old;
878
879 if (where < 0)
880 where = cf->menu->nitems - 1;
881 else if ((size_t) where > cf->menu->nitems - 1)
882 where = 0;
883 if ((size_t) where == cf->menu_idx)
884 return;
6dbe3af9 885
1af8003b
KZ
886 ui_clean_info();
887
8c3a5a44
KZ
888 old = cf->menu_idx;
889 cf->menu_idx = where;
6dbe3af9 890
8c3a5a44
KZ
891 d = menu_get_menuitem(cf, old);
892 ui_draw_menuitem(cf, d, old);
6dbe3af9 893
8c3a5a44
KZ
894 d = menu_get_menuitem(cf, where);
895 ui_draw_menuitem(cf, d, where);
6dbe3af9
KZ
896}
897
ac27ea5c
KZ
898static int ui_menu_move(struct cfdisk *cf, int key)
899{
900 assert(cf);
901 assert(cf->menu);
902
903 if (cf->menu->vertical)
904 {
905 switch (key) {
906 case KEY_DOWN:
907 case '\016': /* ^N */
908 case 'j': /* Vi-like alternative */
909 ui_menu_goto(cf, cf->menu_idx + 1);
910 return 0;
911 case KEY_UP:
912 case '\020': /* ^P */
913 case 'k': /* Vi-like alternative */
914 ui_menu_goto(cf, cf->menu_idx - 1);
915 return 0;
916 case KEY_HOME:
917 ui_menu_goto(cf, 0);
918 return 0;
919 case KEY_END:
920 ui_menu_goto(cf, cf->menu->nitems);
921 return 0;
922 }
923 } else {
924 switch (key) {
925 case KEY_RIGHT:
926 case '\t':
927 ui_menu_goto(cf, cf->menu_idx + 1);
928 return 0;
929 case KEY_LEFT:
930#ifdef KEY_BTAB
931 case KEY_BTAB:
932#endif
933 ui_menu_goto(cf, cf->menu_idx - 1);
934 return 0;
935 }
936 }
937
938 return 1; /* key irrelevant for menu move */
939}
940
8c3a5a44
KZ
941static void ui_draw_partition(struct cfdisk *cf, size_t i)
942{
943 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
944 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
6dbe3af9 945
8c3a5a44 946 DBG(FRONTEND, dbgprint("ui: draw partition %zu", i));
6dbe3af9 947
8c3a5a44
KZ
948 if (cf->lines_idx == i) {
949 standout();
950 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
951 mvaddstr(ln, cl, cf->lines[i + 1]);
952 standend();
6dbe3af9 953 } else {
45333e9d
KZ
954 int at = 0;
955
956 if (is_freespace(cf, i)) {
957 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
958 at = 1;
959 }
8c3a5a44
KZ
960 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
961 mvaddstr(ln, cl, cf->lines[i + 1]);
45333e9d
KZ
962 if (at)
963 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
6dbe3af9
KZ
964 }
965
6dbe3af9
KZ
966}
967
8c3a5a44
KZ
968static int ui_draw_table(struct cfdisk *cf)
969{
970 int cl = ARROW_CURSOR_WIDTH;
971 size_t i, nparts = fdisk_table_get_nents(cf->table);
7eda085c 972
8c3a5a44 973 DBG(FRONTEND, dbgprint("ui: draw table"));
6dbe3af9 974
8c3a5a44
KZ
975 if (cf->nlines - 2 < cf->lines_idx)
976 cf->lines_idx = cf->nlines - 2; /* don't count header */
6dbe3af9 977
8c3a5a44
KZ
978 /* print header */
979 attron(A_BOLD);
980 mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
981 attroff(A_BOLD);
6dbe3af9 982
8c3a5a44
KZ
983 /* print partitions */
984 for (i = 0; i < nparts; i++)
985 ui_draw_partition(cf, i);
6dbe3af9 986
8c3a5a44 987 return 0;
6dbe3af9
KZ
988}
989
8c3a5a44
KZ
990static int ui_table_goto(struct cfdisk *cf, int where)
991{
992 size_t old;
993 size_t nparts = fdisk_table_get_nents(cf->table);
6dbe3af9 994
8c3a5a44 995 DBG(FRONTEND, dbgprint("ui: goto table %d", where));
6dbe3af9 996
8c3a5a44
KZ
997 if (where < 0)
998 where = 0;
999 else if ((size_t) where > nparts - 1)
1000 where = nparts - 1;
6dbe3af9 1001
8c3a5a44
KZ
1002 if ((size_t) where == cf->lines_idx)
1003 return 0;
6dbe3af9 1004
8c3a5a44
KZ
1005 old = cf->lines_idx;
1006 cf->lines_idx = where;
6dbe3af9 1007
8c3a5a44
KZ
1008 ui_draw_partition(cf, old); /* cleanup old */
1009 ui_draw_partition(cf, where); /* draw new */
1af8003b 1010 ui_clean_info();
8c3a5a44
KZ
1011 ui_draw_menu(cf);
1012 refresh();
1013 return 0;
6dbe3af9
KZ
1014}
1015
8c3a5a44
KZ
1016static int ui_refresh(struct cfdisk *cf)
1017{
1018 char *id = NULL;
1019 uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size;
1020 char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
1021 | SIZE_SUFFIX_3LETTER, bytes);
1022 erase();
1023
83fa0f80 1024 if (!ui_enabled)
8c3a5a44
KZ
1025 return -EINVAL;
1026
1027 /* header */
1028 attron(A_BOLD);
1af8003b 1029 ui_center(0, _("Disk: %s"), cf->cxt->dev_path);
8c3a5a44 1030 attroff(A_BOLD);
1af8003b 1031 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
8c3a5a44
KZ
1032 strsz, bytes, (uintmax_t) cf->cxt->total_sectors);
1033 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1af8003b 1034 ui_center(2, _("Label: %s, identifier: %s"),
8c3a5a44 1035 cf->cxt->label->name, id);
7eda085c 1036 else
1af8003b 1037 ui_center(2, _("Label: %s"));
8c3a5a44 1038 free(strsz);
6dbe3af9 1039
8c3a5a44
KZ
1040 ui_draw_table(cf);
1041 ui_draw_menu(cf);
6dbe3af9 1042 refresh();
8c3a5a44 1043 return 0;
6dbe3af9
KZ
1044}
1045
b1f58330
KZ
1046static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
1047 const char *hint, char *buf, size_t len)
1048{
1049 size_t cells = 0;
1050 ssize_t i = 0, rc = -1;
1051 wint_t c;
1052 int ln = MENU_START_LINE, cl = 1;
1053
1054 assert(cf);
1055 assert(buf);
1056 assert(len);
1057
1058 move(ln, 0);
1059 clrtoeol();
1060
1061 if (prompt) {
1062 mvaddstr(ln, cl, prompt);
1063 cl += mbs_safe_width(prompt);
1064 }
1065
1066 /* default value */
1067 if (*buf) {
1068 i = strlen(buf);
1069 cells = mbs_safe_width(buf);
1070 mvaddstr(ln, cl, buf);
1071 }
1072
1073 if (hint)
1074 ui_hint(hint);
1075 else
1076 ui_clean_hint();
1077
1078 move(ln, cl + cells);
1079 curs_set(1);
1080 refresh();
1081
1082 while (1) {
1083#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1084 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1085 if (get_wch(&c) == ERR) {
1086#else
1087 if ((c = getch()) == ERR) {
1088#endif
1089 if (!isatty(STDIN_FILENO))
1090 exit(2);
1091 else
1092 goto done;
1093 }
1094 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1095 break;
1096
1097 switch (c) {
1098 case KEY_ESC:
1099 rc = -CFDISK_ERR_ESC;
1100 goto done;
1101 case KEY_DELETE:
1102 case '\b':
1103 case KEY_BACKSPACE:
1104 if (i > 0) {
1105 cells--;
1106 i = mbs_truncate(buf, &cells);
1107 if (i < 0)
1108 goto done;
1109 mvaddch(ln, cl + cells, ' ');
1110 move(ln, cl + cells);
1111 } else
1112 beep();
1113 break;
1114 default:
1115#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1116 if (i + 1 < (ssize_t) len && iswprint(c)) {
1117 wchar_t wc = (wchar_t) c;
1118 char s[MB_CUR_MAX + 1];
1119 int sz = wctomb(s, wc);
1120
1121 if (sz > 0 && sz + i < (ssize_t) len) {
1122 s[sz] = '\0';
1123 mvaddnstr(ln, cl + cells, s, sz);
1124 memcpy(buf + i, s, sz);
1125 i += sz;
1126 buf[i] = '\0';
1127 cells += wcwidth(wc);
1128 } else
1129 beep();
1130 }
1131#else
1132 if (i + 1 < (ssize_t) len && isprint(c)) {
1133 mvaddch(ln, cl + cells, c);
1134 str[i++] = c;
1135 str[i] = '\0';
1136 cells++;
1137 }
1138#endif
1139 else
1140 beep();
1141 }
1142 refresh();
1143 }
1144
1145 rc = i; /* success */
1146done:
1147 move(ln, 0);
1148 clrtoeol();
1149 curs_set(0);
1150 refresh();
1151
1152 return rc;
1153}
1154
1155/* @res is default value as well as result in bytes */
1156static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
1157 uintmax_t low, uintmax_t up)
1158{
1159 char buf[128];
1160 uintmax_t user = 0;
1161 ssize_t rc;
1162 char *dflt = size_to_human_string(0, *res);
1163
1164 DBG(FRONTEND, dbgprint("ui: get_size (default=%ju)", *res));
1165
1166 ui_clean_info();
1167
1168 do {
91ba41ca 1169 int pwr = 0, insec = 0;
b1f58330
KZ
1170
1171 snprintf(buf, sizeof(buf), "%s", dflt);
1172 rc = ui_get_string(cf, prompt,
5139eca7 1173 _("May be followed by {M,B,G,T}iB "
91ba41ca 1174 "(the \"iB\" is optional) or S for sectors."),
b1f58330
KZ
1175 buf, sizeof(buf));
1176 if (rc == 0) {
1177 ui_warnx(_("Please, specify size."));
1178 continue; /* nothing specified */
1179 } else if (rc == -CFDISK_ERR_ESC)
1180 break; /* cancel dialog */
1181
91ba41ca
KZ
1182 if (strcmp(buf, dflt) == 0)
1183 user = *res, rc = 0; /* no change, use default */
1184 else {
1185 size_t len = strlen(buf);
1186 if (buf[len - 1] == 'S') {
1187 insec = 1;
1188 buf[len - 1] = '\0';
1189 }
1190 rc = parse_size(buf, &user, &pwr); /* parse */
1191 }
1192
b1f58330 1193 if (rc == 0) {
91ba41ca
KZ
1194 DBG(FRONTEND, dbgprint("ui: get_size user=%ju, power=%d, sectors=%s",
1195 user, pwr, insec ? "yes" : "no"));
1196 if (insec)
1197 user *= cf->cxt->sector_size;
b1f58330
KZ
1198 if (user < low) {
1199 ui_warnx(_("Minimal size is %ju"), low);
1200 rc = -ERANGE;
1201 }
1202 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1203 /* ignore when the user specified size overflow
1204 * with in range specified by suffix (e.g. MiB) */
1205 user = up;
1206
1207 if (user > up) {
1208 ui_warnx(_("Maximal size is %ju bytes."), up);
1209 rc = -ERANGE;
1210 }
2cec7949
KZ
1211 } else
1212 ui_warnx(_("Failed to parse size."));
b1f58330
KZ
1213 } while (rc != 0);
1214
1215 if (rc == 0)
1216 *res = user;
1217 free(dflt);
1218
1219 DBG(FRONTEND, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res, rc));
1220 return rc;
1221}
1222
7aa0d529
KZ
1223/* prints menu with libfdisk labels and waits for users response */
1224static int ui_create_label(struct cfdisk *cf)
1225{
1226 struct cfdisk_menudesc *d, *cm;
1227 int rc = 1;
1228 size_t i = 0, nitems;
1229 struct fdisk_label *lb = NULL;
1230
1231 assert(cf);
1232
1233 DBG(FRONTEND, dbgprint("ui: asking for new disklabe."));
1234
1235 /* create cfdisk menu according to libfdisk labels, note that the
1236 * last cm[] item has to be empty -- so nitems + 1 */
1237 nitems = fdisk_context_get_nlabels(cf->cxt);
1238 cm = calloc(nitems + 1, sizeof(struct cfdisk_menudesc));
1239 if (!cm)
1240 return -ENOMEM;
1241
1242 for (i = 0; i < nitems; i++) {
1243 if (fdisk_context_next_label(cf->cxt, &lb))
1244 break;
1245 cm[i].name = lb->name;
1246 }
1247
1248 erase();
1249 ui_center(LINES - 4,
1250 _("Device does not contain a recognized partition table."));
1251 ui_center(LINES - 3,
1252 _("Please, select a type to create a new disk label."));
1253
1254 /* make the new menu active */
1255 menu_push(cf, CFDISK_MENU_GENERATED, cm);
1256 cf->menu->vertical = 1;
1257 menu_set_title(cf->menu, _("Select label type"));
1258 ui_draw_menu(cf);
1259 refresh();
1260
1261 do {
1262 int key = getch();
1263 if (ui_menu_move(cf, key) == 0)
1264 continue;
1265 switch (key) {
1266 case KEY_ENTER:
1267 case '\n':
1268 case '\r':
1269 d = menu_get_menuitem(cf, cf->menu_idx);
1270 if (d)
1271 rc = fdisk_create_disklabel(cf->cxt, d->name);
1272 goto done;
1273 case 'q':
1274 case 'Q':
1275 goto done;
1276 }
1277 } while (1);
1278
1279done:
1280 menu_pop(cf);
1281 free(cm);
1282 DBG(FRONTEND, dbgprint("ui: create label done [rc=%d] ", rc));
1283 return rc;
1284}
1285
ed8a13e6
KZ
1286/* returns: error: < 0, success: 0, quit: 1 */
1287static int main_menu_action(struct cfdisk *cf, int key)
1288{
1289 size_t n;
1290 int ref = 0, rc;
1291 const char *info = NULL, *warn = NULL;
1292 struct fdisk_partition *pa;
1293
1294 assert(cf);
1295 assert(cf->cxt);
1296 assert(cf->menu);
1297
1298 if (key == 0) {
1299 struct cfdisk_menudesc *d = menu_get_menuitem(cf, cf->menu_idx);
1300 if (!d)
1301 return 0;
1302 key = d->key;
1303
1304 } else if (key != 'w' && key != 'W')
1305 key = tolower(key); /* case insensitive except 'W'rite */
1306
1307 DBG(FRONTEND, dbgprint("ui: main menu action: key=%c", key));
1308
1309 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
1310 DBG(FRONTEND, dbgprint(" ignore '%c'", key));
1311 return 0;
1312 }
1313
1314 pa = get_current_partition(cf);
1315 n = fdisk_partition_get_partno(pa);
1316
1317 DBG(FRONTEND, dbgprint("menu action on %p", pa));
1318
1319 switch (key) {
1320 case 'b': /* Bootable flag */
1321 {
1322 int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
1323 fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
1324
1325 if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl))
1326 warn = _("Could not toggle the flag.");
1327 else if (fl)
1328 ref = 1;
1329 break;
1330 }
1331 case 'd': /* Delete */
1332 if (fdisk_delete_partition(cf->cxt, n) != 0)
1333 warn = _("Could not delete partition %zu.");
1334 else
1335 info = _("Partition %zu has been deleted.");
1336 ref = 1;
1337 break;
1338 case 'n': /* New */
1339 {
1340 uint64_t start, size, dflt_size;
1341 struct fdisk_partition *npa; /* the new partition */
1342
1343 if (!pa || !fdisk_partition_is_freespace(pa))
1344 return -EINVAL;
1345 npa = fdisk_new_partition();
1346 if (!npa)
1347 return -ENOMEM;
1348 /* free space range */
1349 start = fdisk_partition_get_start(pa);
1350 size = dflt_size = fdisk_partition_get_size(pa) * cf->cxt->sector_size;
1351
1352 if (ui_get_size(cf, _("Partition size: "), &size, 1, size)
1353 == -CFDISK_ERR_ESC)
1354 break;
1355
1356 if (dflt_size == size) /* default is to fillin all free space */
1357 fdisk_partition_end_follow_default(npa, 1);
1358 else /* set relative size of the partition */
1359 fdisk_partition_set_size(npa, size / cf->cxt->sector_size);
1360
1361 fdisk_partition_set_start(npa, start);
1362 fdisk_partition_partno_follow_default(npa, 1);
1363 /* add to disk label -- libfdisk will ask for missing details */
1364 rc = fdisk_add_partition(cf->cxt, npa);
1365 fdisk_unref_partition(npa);
1366 if (rc == 0)
1367 ref = 1;
1368 break;
1369 }
1370 case 'q': /* Quit */
1371 return 1;
1372 case 't': /* Type */
1373 break;
1374 case 'W': /* Write */
1375 rc = fdisk_write_disklabel(cf->cxt);
1376 if (rc)
1377 ui_errx(EXIT_FAILURE, _("failed to write disklabel"));
1378 fdisk_reread_partition_table(cf->cxt);
1379 ui_info(_("The partition table has been altered."));
1380 break;
1381 }
1382
1383 if (ref) {
1384 lines_refresh(cf);
1385 ui_refresh(cf);
1386 }
1387 if (warn)
1388 ui_warnx(warn, n);
1389 else if (info)
1390 ui_info(info, n);
1391
1392 return 0;
1393}
1394
1395
8c3a5a44
KZ
1396static int ui_run(struct cfdisk *cf)
1397{
7aa0d529 1398 int rc = 0;
6dbe3af9 1399
8c3a5a44 1400 DBG(FRONTEND, dbgprint("ui: start COLS=%d, LINES=%d", COLS, LINES));
6dbe3af9 1401
7aa0d529
KZ
1402 if (!fdisk_dev_has_disklabel(cf->cxt)) {
1403 rc = ui_create_label(cf);
1404 if (rc < 0)
1405 ui_errx(EXIT_FAILURE,
1406 _("failed to create a new disklabel"));
1407 if (rc)
1408 return rc;
1409 }
1410
1411 cols_init(cf);
1412 rc = lines_refresh(cf);
1413 if (rc)
1414 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
6dbe3af9 1415
7aa0d529 1416 menu_push(cf, CFDISK_MENU_MAIN, NULL);
8c3a5a44
KZ
1417 rc = ui_refresh(cf);
1418 if (rc)
1419 return rc;
6dbe3af9 1420
8c3a5a44 1421 do {
8460875d 1422 int rc = 0, key = getch();
6dbe3af9 1423
ac27ea5c
KZ
1424 if (ui_menu_move(cf, key) == 0)
1425 continue;
1426
8c3a5a44
KZ
1427 switch (key) {
1428 case KEY_DOWN:
1429 case '\016': /* ^N */
1430 case 'j': /* Vi-like alternative */
1431 ui_table_goto(cf, cf->lines_idx + 1);
1432 break;
1433 case KEY_UP:
1434 case '\020': /* ^P */
1435 case 'k': /* Vi-like alternative */
1436 ui_table_goto(cf, cf->lines_idx - 1);
1437 break;
1438 case KEY_HOME:
1439 ui_table_goto(cf, 0);
1440 break;
1441 case KEY_END:
1442 ui_table_goto(cf, cf->nlines - 1);
1443 break;
8c3a5a44
KZ
1444 case KEY_ENTER:
1445 case '\n':
1446 case '\r':
ed8a13e6 1447 rc = main_menu_action(cf, 0);
8c3a5a44
KZ
1448 break;
1449 default:
ed8a13e6 1450 rc = main_menu_action(cf, key);
8460875d 1451 if (rc < 0)
8c3a5a44
KZ
1452 beep();
1453 break;
1454 }
8460875d
KZ
1455
1456 if (rc == 1)
1457 break; /* quit */
8c3a5a44 1458 } while (1);
6dbe3af9 1459
8c3a5a44 1460 menu_pop(cf);
6dbe3af9 1461
8c3a5a44
KZ
1462 DBG(FRONTEND, dbgprint("ui: end"));
1463
1464 return 0;
1465}
6dbe3af9 1466
8c3a5a44
KZ
1467int main(int argc, char *argv[])
1468{
ed8a13e6 1469 int rc;
8c3a5a44
KZ
1470 struct cfdisk _cf = { .lines_idx = 0 },
1471 *cf = &_cf;
6dbe3af9 1472
8c3a5a44
KZ
1473 setlocale(LC_ALL, "");
1474 bindtextdomain(PACKAGE, LOCALEDIR);
1475 textdomain(PACKAGE);
1476 atexit(close_stdout);
6dbe3af9 1477
8c3a5a44
KZ
1478 fdisk_init_debug(0);
1479 cf->cxt = fdisk_new_context();
1480 if (!cf->cxt)
1481 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
6dbe3af9 1482
8c3a5a44 1483 fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf);
6dbe3af9 1484
8c3a5a44
KZ
1485 if (argc != 2)
1486 err(EXIT_FAILURE, "usage: %s <device>", argv[0]);
6dbe3af9 1487
8c3a5a44
KZ
1488 if (fdisk_context_assign_device(cf->cxt, argv[optind], 0) != 0)
1489 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
6dbe3af9 1490
8c3a5a44
KZ
1491 /* Don't use err(), warn() from this point */
1492 ui_init(cf);
1493 ui_run(cf);
7aa0d529 1494 ui_end();
6dbe3af9 1495
8c3a5a44
KZ
1496 free(cf->lines);
1497 free(cf->linesbuf);
1498 fdisk_unref_table(cf->table);
ed8a13e6
KZ
1499
1500 rc = fdisk_context_deassign_device(cf->cxt);
8c3a5a44 1501 fdisk_free_context(cf->cxt);
ed8a13e6
KZ
1502 DBG(FRONTEND, dbgprint("bye! [rc=%d]", rc));
1503 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6dbe3af9 1504}