]> git.ipfire.org Git - thirdparty/util-linux.git/blob - disk-utils/cfdisk.c
cfdisk: clean up scols usage
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
1 #include <stdlib.h>
2 #include <stdio.h>
3 #include <errno.h>
4 #include <signal.h>
5 #include <ctype.h>
6 #include <getopt.h>
7 #include <libsmartcols.h>
8
9 #ifdef HAVE_SLANG_H
10 # include <slang.h>
11 #elif defined(HAVE_SLANG_SLANG_H)
12 # include <slang/slang.h>
13 #endif
14
15 #ifdef HAVE_SLCURSES_H
16 # include <slcurses.h>
17 #elif defined(HAVE_SLANG_SLCURSES_H)
18 # include <slang/slcurses.h>
19 #elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
20 # include <ncursesw/ncurses.h>
21 #elif defined(HAVE_NCURSES_H)
22 # include <ncurses.h>
23 #elif defined(HAVE_NCURSES_NCURSES_H)
24 # include <ncurses/ncurses.h>
25 #endif
26
27 #ifdef HAVE_WIDECHAR
28 # include <wctype.h>
29 # include <wchar.h>
30 #endif
31
32 #include "c.h"
33 #include "closestream.h"
34 #include "nls.h"
35 #include "strutils.h"
36 #include "xalloc.h"
37 #include "mbsalign.h"
38 #include "colors.h"
39
40 #include "fdiskP.h"
41
42 #define ARROW_CURSOR_STRING ">> "
43 #define ARROW_CURSOR_DUMMY " "
44 #define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
45
46 #define MENU_PADDING 2
47 #define TABLE_START_LINE 4
48 #define MENU_START_LINE ((size_t) LINES - 5)
49 #define INFO_LINE ((size_t) LINES - 2)
50 #define HINT_LINE ((size_t) LINES - 1)
51
52 #define CFDISK_ERR_ESC 5000
53
54 #ifndef KEY_ESC
55 # define KEY_ESC '\033'
56 #endif
57 #ifndef KEY_DELETE
58 # define KEY_DELETE '\177'
59 #endif
60
61 /* colors */
62 enum {
63 CFDISK_CL_NONE = 0,
64 CFDISK_CL_WARNING,
65 CFDISK_CL_FREESPACE,
66 };
67 static const int color_pairs[][2] = {
68 /* color foreground, background */
69 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
70 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 }
71 };
72
73 struct cfdisk;
74
75 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
76 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
77 static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
78 static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
79
80 static int ui_refresh(struct cfdisk *cf);
81 static void ui_warnx(const char *fmt, ...);
82 static void ui_warn(const char *fmt, ...);
83 static void ui_info(const char *fmt, ...);
84 static void ui_draw_menu(struct cfdisk *cf);
85 static int ui_menu_move(struct cfdisk *cf, int key);
86
87 static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
88 uintmax_t low, uintmax_t up);
89
90 static int ui_enabled;
91
92 /* menu item */
93 struct cfdisk_menuitem {
94 int key; /* keyboard shortcut */
95 const char *name; /* item name */
96 const char *desc; /* item description (hint) */
97 void *userdata;
98 };
99
100 /* menu */
101 struct cfdisk_menu {
102 char *title; /* optional menu title */
103 struct cfdisk_menuitem *items; /* array with menu items */
104 char *ignore;/* string with keys to ignore */
105 size_t width; /* maximal width of the menu item */
106 size_t nitems; /* number of the active menu items */
107 size_t page_sz;/* when menu longer than screen */
108 size_t idx; /* the current menu item */
109 struct cfdisk_menu *prev;
110
111 /* @ignore keys generator */
112 int (*ignore_cb) (struct cfdisk *, char *, size_t);
113
114 unsigned int vertical : 1; /* enable vertical mode */
115 };
116
117 /* main menu */
118 static struct cfdisk_menuitem main_menuitems[] = {
119 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
120 { 'd', N_("Delete"), N_("Delete the current partition") },
121 { 'n', N_("New"), N_("Create new partition from free space") },
122 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
123 { 't', N_("Type"), N_("Change the partition type") },
124 { 'h', N_("Help"), N_("Print help screen") },
125 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
126 { 0, NULL, NULL }
127 };
128
129 /* top level control struct */
130 struct cfdisk {
131 struct fdisk_context *cxt; /* libfdisk context */
132 struct fdisk_table *table; /* partition table */
133 struct cfdisk_menu *menu; /* the current menu */
134
135 int *cols; /* output columns */
136 size_t ncols; /* number of columns */
137
138 char *linesbuf; /* table as string */
139 size_t linesbufsz; /* size of the tb_buf */
140
141 char **lines; /* array with lines */
142 size_t nlines; /* number of lines */
143 size_t lines_idx; /* current line <0..N>, exclude header */
144 size_t page_sz;
145 };
146
147 /* Initialize output columns -- we follow libcfdisk columns (usually specific
148 * to the label type.
149 */
150 static int cols_init(struct cfdisk *cf)
151 {
152 assert(cf);
153
154 free(cf->cols);
155 cf->cols = NULL;
156 cf->ncols = 0;
157
158 return fdisk_get_columns(cf->cxt, 0, &cf->cols, &cf->ncols);
159 }
160
161 /* Reads partition in tree-like order from scols
162 */
163 static int partition_from_scols(struct fdisk_table *tb,
164 struct libscols_line *ln)
165 {
166 struct fdisk_partition *pa = scols_line_get_userdata(ln);
167
168 fdisk_table_add_partition(tb, pa);
169 fdisk_unref_partition(pa);
170
171 if (scols_line_has_children(ln)) {
172 struct libscols_line *chln;
173 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
174
175 if (!itr)
176 return -EINVAL;
177 while (scols_line_next_child(ln, itr, &chln) == 0)
178 partition_from_scols(tb, chln);
179 }
180 return 0;
181 }
182
183 /* It would be possible to use fdisk_table_to_string(), but we want some
184 * extension to the output format, so let's do it without libfdisk
185 */
186 static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
187 {
188 const struct fdisk_column *col;
189 struct fdisk_partition *pa;
190 struct fdisk_label *lb;
191 struct fdisk_iter *itr = NULL;
192 struct libscols_table *table = NULL;
193 struct libscols_iter *s_itr = NULL;
194 char *res = NULL;
195 size_t i;
196 int tree = 0;
197 struct libscols_line *ln, *ln_cont = NULL;
198
199 DBG(FRONTEND, ul_debug("table: convert to string"));
200
201 assert(cf);
202 assert(cf->cxt);
203 assert(cf->cols);
204 assert(tb);
205
206 lb = fdisk_context_get_label(cf->cxt, NULL);
207 assert(lb);
208
209 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
210 if (!itr)
211 goto done;
212
213 /* get container (e.g. extended partition) */
214 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
215 if (fdisk_partition_is_nested(pa)) {
216 DBG(FRONTEND, ul_debug("table: nested detected, using tree"));
217 tree = SCOLS_FL_TREE;
218 break;
219 }
220 }
221
222 table = scols_new_table(NULL);
223 if (!table)
224 goto done;
225 scols_table_set_max(table, 1);
226 scols_table_set_tree(table, tree);
227
228 /* headers */
229 for (i = 0; i < cf->ncols; i++) {
230 col = fdisk_label_get_column(lb, cf->cols[i]);
231 if (col) {
232 int fl = col->scols_flags;
233 if (tree && col->id == FDISK_COL_DEVICE)
234 fl |= SCOLS_FL_TREE;
235 if (!scols_table_new_column(table, col->name, col->width, fl))
236 goto done;
237 }
238 }
239
240 /* data */
241 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
242
243 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
244 struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
245
246 ln = scols_table_new_line(table, parent);
247 if (!ln)
248 goto done;
249 for (i = 0; i < cf->ncols; i++) {
250 char *cdata = NULL;
251 col = fdisk_label_get_column(lb, cf->cols[i]);
252 if (!col)
253 continue;
254 if (fdisk_partition_to_string(pa, cf->cxt, col->id, &cdata))
255 continue;
256 scols_line_refer_data(ln, i, cdata);
257 }
258 if (tree && fdisk_partition_is_container(pa))
259 ln_cont = ln;
260
261 scols_line_set_userdata(ln, (void *) pa);
262 fdisk_ref_partition(pa);
263 }
264
265 if (scols_table_is_empty(table))
266 goto done;
267
268 scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
269 scols_print_table_to_string(table, &res);
270
271 /* scols_* code might reorder lines, let's reorder @tb according to the
272 * final output (it's no problem because partitions are addressed by
273 * parno stored within struct fdisk_partition) */
274
275 /* remove all */
276 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
277 while (fdisk_table_next_partition(tb, itr, &pa) == 0)
278 fdisk_table_remove_partition(tb, pa);
279
280 s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
281 if (!s_itr)
282 goto done;
283
284 /* add all in the right order (don't forget the output is tree) */
285 while (scols_table_next_line(table, s_itr, &ln) == 0) {
286 if (scols_line_get_parent(ln))
287 continue;
288 if (partition_from_scols(tb, ln))
289 break;
290 }
291 done:
292 scols_unref_table(table);
293 scols_free_iter(s_itr);
294 fdisk_free_iter(itr);
295
296 return res;
297 }
298
299 /*
300 * Read data about partitions from libfdisk and prepare output lines.
301 */
302 static int lines_refresh(struct cfdisk *cf)
303 {
304 int rc;
305 char *p;
306 size_t i;
307
308 assert(cf);
309
310 DBG(FRONTEND, ul_debug("refreshing buffer"));
311
312 free(cf->linesbuf);
313 free(cf->lines);
314 cf->linesbuf = NULL;
315 cf->linesbufsz = 0;
316 cf->lines = NULL;
317 cf->nlines = 0;
318
319 fdisk_unref_table(cf->table);
320 cf->table = NULL;
321
322 /* read partitions and free spaces into cf->table */
323 rc = fdisk_get_partitions(cf->cxt, &cf->table);
324 if (!rc)
325 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
326 if (rc)
327 return rc;
328
329 cf->linesbuf = table_to_string(cf, cf->table);
330 if (!cf->linesbuf)
331 return -ENOMEM;
332
333 cf->linesbufsz = strlen(cf->linesbuf);
334 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
335 cf->page_sz = 0;
336
337 if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
338 cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
339
340 cf->lines = xcalloc(cf->nlines, sizeof(char *));
341
342 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
343 cf->lines[i] = p;
344 p = strchr(p, '\n');
345 if (p) {
346 *p = '\0';
347 p++;
348 }
349 }
350
351 return 0;
352 }
353
354 static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
355 {
356 assert(cf);
357 assert(cf->table);
358
359 return fdisk_table_get_partition(cf->table, cf->lines_idx);
360 }
361
362 static int is_freespace(struct cfdisk *cf, size_t i)
363 {
364 struct fdisk_partition *pa;
365
366 assert(cf);
367 assert(cf->table);
368
369 pa = fdisk_table_get_partition(cf->table, i);
370 return fdisk_partition_is_freespace(pa);
371 }
372
373 /* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
374 * responseback to libfdisk
375 */
376 static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
377 {
378 struct cfdisk_menuitem *d, *cm;
379 int key;
380 size_t i = 0, nitems;
381 const char *name, *desc;
382
383 assert(ask);
384 assert(cf);
385
386 /* create cfdisk menu according to libfdisk ask-menu, note that the
387 * last cm[] item has to be empty -- so nitems + 1 */
388 nitems = fdisk_ask_menu_get_nitems(ask);
389 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
390
391 for (i = 0; i < nitems; i++) {
392 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
393 break;
394 cm[i].key = key;
395 cm[i].desc = desc;
396 cm[i].name = name;
397 }
398
399 /* make the new menu active */
400 menu_push(cf, cm);
401 ui_draw_menu(cf);
402 refresh();
403
404 /* wait for keys */
405 do {
406 int key = getch();
407
408 if (ui_menu_move(cf, key) == 0)
409 continue;
410
411 switch (key) {
412 case KEY_ENTER:
413 case '\n':
414 case '\r':
415 d = menu_get_menuitem(cf, cf->menu->idx);
416 if (d)
417 fdisk_ask_menu_set_result(ask, d->key);
418 menu_pop(cf);
419 free(cm);
420 return 0;
421 }
422 } while (1);
423
424 menu_pop(cf);
425 free(cm);
426 return -1;
427 }
428
429 /* libfdisk callback
430 */
431 static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
432 void *data __attribute__((__unused__)))
433 {
434 int rc = 0;
435
436 assert(cxt);
437 assert(ask);
438
439 switch(fdisk_ask_get_type(ask)) {
440 case FDISK_ASKTYPE_INFO:
441 ui_info(fdisk_ask_print_get_mesg(ask));
442 break;
443 case FDISK_ASKTYPE_WARNX:
444 ui_warnx(fdisk_ask_print_get_mesg(ask));
445 break;
446 case FDISK_ASKTYPE_WARN:
447 ui_warn(fdisk_ask_print_get_mesg(ask));
448 break;
449 case FDISK_ASKTYPE_MENU:
450 ask_menu(ask, (struct cfdisk *) data);
451 break;
452 default:
453 ui_warnx(_("internal error: unsupported dialog type %d"),
454 fdisk_ask_get_type(ask));
455 return -EINVAL;
456 }
457 return rc;
458 }
459
460 static int ui_end(void)
461 {
462 if (!ui_enabled)
463 return -EINVAL;
464
465 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
466 SLsmg_gotorc(LINES - 1, 0);
467 SLsmg_refresh();
468 #else
469 mvcur(0, COLS - 1, LINES-1, 0);
470 #endif
471 nl();
472 endwin();
473 printf("\n");
474 ui_enabled = 0;
475 return 0;
476 }
477
478 static void ui_vprint_center(int line, int attrs, const char *fmt, va_list ap)
479 {
480 size_t width;
481 char *buf = NULL;
482
483 move(line, 0);
484 clrtoeol();
485
486 xvasprintf(&buf, fmt, ap);
487
488 width = mbs_safe_width(buf);
489 if (width > (size_t) COLS) {
490 char *p = strrchr(buf + COLS, ' ');
491 if (!p)
492 p = buf + COLS;
493 *p = '\0';
494 if (line + 1 >= LINES)
495 line--;
496 attron(attrs);
497 mvaddstr(line, 0, buf);
498 mvaddstr(line + 1, 0, p+1);
499 attroff(attrs);
500 } else {
501 attron(attrs);
502 mvaddstr(line, (COLS - width) / 2, buf);
503 attroff(attrs);
504 }
505 free(buf);
506 }
507
508 static void ui_center(int line, const char *fmt, ...)
509 {
510 va_list ap;
511 va_start(ap, fmt);
512 ui_vprint_center(line, 0, fmt, ap);
513 va_end(ap);
514 }
515
516 static void ui_warnx(const char *fmt, ...)
517 {
518 va_list ap;
519 va_start(ap, fmt);
520 if (ui_enabled)
521 ui_vprint_center(INFO_LINE,
522 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
523 fmt, ap);
524 else
525 vfprintf(stderr, fmt, ap);
526 va_end(ap);
527 }
528
529 static void ui_warn(const char *fmt, ...)
530 {
531 char *fmt_m;
532 va_list ap;
533
534 xasprintf(&fmt_m, "%s: %m", fmt);
535
536 va_start(ap, fmt);
537 if (ui_enabled)
538 ui_vprint_center(INFO_LINE,
539 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
540 fmt_m, ap);
541 else
542 vfprintf(stderr, fmt_m, ap);
543 va_end(ap);
544 free(fmt_m);
545 }
546
547 static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
548 {
549 va_list ap;
550 ui_end();
551
552 va_start(ap, fmt);
553 fprintf(stderr, "%s: ", program_invocation_short_name);
554 vfprintf(stderr, fmt, ap);
555 va_end(ap);
556
557 exit(rc);
558 }
559
560 static void ui_info(const char *fmt, ...)
561 {
562 va_list ap;
563 va_start(ap, fmt);
564 if (ui_enabled)
565 ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap);
566 else
567 vfprintf(stdout, fmt, ap);
568 va_end(ap);
569 }
570
571 static void ui_clean_info(void)
572 {
573 move(INFO_LINE, 0);
574 clrtoeol();
575 }
576
577 static void ui_hint(const char *fmt, ...)
578 {
579 va_list ap;
580 va_start(ap, fmt);
581 if (ui_enabled)
582 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
583 else
584 vfprintf(stdout, fmt, ap);
585 va_end(ap);
586 }
587
588 static void ui_clean_hint(void)
589 {
590 move(HINT_LINE, 0);
591 clrtoeol();
592 }
593
594 static void die_on_signal(int dummy __attribute__((__unused__)))
595 {
596 DBG(FRONTEND, ul_debug("die on signal."));
597 ui_end();
598 exit(EXIT_FAILURE);
599 }
600
601 static void menu_update_ignore(struct cfdisk *cf)
602 {
603 char ignore[128] = { 0 };
604 int i = 0;
605 struct cfdisk_menu *m;
606 struct cfdisk_menuitem *d, *org;
607 size_t idx;
608
609 assert(cf);
610 assert(cf->menu);
611 assert(cf->menu->ignore_cb);
612
613 m = cf->menu;
614 org = menu_get_menuitem(cf, m->idx);
615
616 DBG(FRONTEND, ul_debug("menu: update menu ignored keys"));
617
618 i = m->ignore_cb(cf, ignore, sizeof(ignore));
619 ignore[i] = '\0';
620
621 /* return if no change */
622 if ( (!m->ignore && !*ignore)
623 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
624 return;
625 }
626
627 free(m->ignore);
628 m->ignore = xstrdup(ignore);
629 m->nitems = 0;
630
631 for (d = m->items; d->name; d++) {
632 if (m->ignore && strchr(m->ignore, d->key))
633 continue;
634 m->nitems++;
635 }
636
637 /* refresh menu index to be at the same menuitem or go to the first */
638 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
639 m->idx = idx;
640 else
641 m->idx = 0;
642
643 m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0;
644 }
645
646 static struct cfdisk_menu *menu_push(
647 struct cfdisk *cf,
648 struct cfdisk_menuitem *items)
649 {
650 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
651 struct cfdisk_menuitem *d;
652
653 assert(cf);
654
655 DBG(FRONTEND, ul_debug("menu: new menu"));
656
657 m->prev = cf->menu;
658 m->items = items;
659
660 for (d = m->items; d->name; d++) {
661 const char *name = _(d->name);
662 size_t len = mbs_safe_width(name);
663 if (len > m->width)
664 m->width = len;
665 m->nitems++;
666 }
667
668 cf->menu = m;
669 m->page_sz = m->nitems / (LINES - 4) ? LINES - 4 : 0;
670 return m;
671 }
672
673 static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
674 {
675 struct cfdisk_menu *m = NULL;
676
677 assert(cf);
678
679 DBG(FRONTEND, ul_debug("menu: rem menu"));
680
681 if (cf->menu) {
682 m = cf->menu->prev;
683 free(cf->menu->ignore);
684 free(cf->menu->title);
685 free(cf->menu);
686 }
687 cf->menu = m;
688 return cf->menu;
689 }
690
691 static void menu_set_title(struct cfdisk_menu *m, const char *title)
692 {
693 char *str = NULL;
694
695 if (title) {
696 size_t len = mbs_safe_width(title);
697 if (len + 3 > m->width)
698 m->width = len + 3;
699 str = xstrdup(title);
700 }
701 m->title = str;
702 }
703
704
705 static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
706 {
707 struct sigaction sa;
708
709 DBG(FRONTEND, ul_debug("ui: init"));
710
711 /* setup SIGCHLD handler */
712 sigemptyset(&sa.sa_mask);
713 sa.sa_flags = 0;
714 sa.sa_handler = die_on_signal;
715 sigaction(SIGINT, &sa, NULL);
716 sigaction(SIGTERM, &sa, NULL);
717
718 ui_enabled = 1;
719 initscr();
720
721 #ifdef HAVE_USE_DEFAULT_COLORS
722 if (colors_wanted() && has_colors()) {
723 size_t i;
724
725 start_color();
726 use_default_colors();
727 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
728 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
729 }
730 #else
731 colors_init(UL_COLORMODE_NEVER, "cfdisk");
732 #endif
733
734 cbreak();
735 noecho();
736 nonl();
737 curs_set(0);
738 keypad(stdscr, TRUE);
739
740 return 0;
741 }
742
743 static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
744 {
745 struct cfdisk_menu *m = cf->menu;
746
747 if (m->vertical) {
748 if (!m->page_sz) /* small menu */
749 return (LINES - (cf->menu->nitems + 1)) / 2 + idx;
750 return (idx % m->page_sz) + 1;
751 } else {
752 size_t len = m->width + 4 + MENU_PADDING; /* item width */
753 size_t items = COLS / len; /* items per line */
754
755 return MENU_START_LINE + ((idx / items));
756 }
757 }
758
759 static int menuitem_get_column(struct cfdisk *cf, size_t idx)
760 {
761 if (cf->menu->vertical) {
762 size_t nc = cf->menu->width + MENU_PADDING;
763 if ((size_t) COLS <= nc)
764 return 0;
765 return (COLS - nc) / 2;
766 } else {
767 size_t len = cf->menu->width + 4 + MENU_PADDING; /* item width */
768 size_t items = COLS / len; /* items per line */
769 size_t extra = items < cf->menu->nitems ? /* extra space on line */
770 COLS % len : /* - multi-line menu */
771 COLS - (cf->menu->nitems * len); /* - one line menu */
772
773 extra += MENU_PADDING; /* add padding after last item to extra */
774
775 if (idx < items)
776 return (idx * len) + (extra / 2);
777 return ((idx % items) * len) + (extra / 2);
778 }
779 }
780
781 static int menuitem_on_page(struct cfdisk *cf, size_t idx)
782 {
783 struct cfdisk_menu *m = cf->menu;
784
785 if (m->page_sz == 0 ||
786 m->idx / m->page_sz == idx / m->page_sz)
787 return 1;
788 return 0;
789 }
790
791 static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
792 {
793 struct cfdisk_menuitem *d;
794 size_t i;
795
796 for (i = 0, d = cf->menu->items; d->name; d++) {
797 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
798 continue;
799 if (i++ == idx)
800 return d;
801 }
802
803 return NULL;
804 }
805
806 static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
807 int key, size_t *idx)
808 {
809 struct cfdisk_menuitem *d;
810
811 for (*idx = 0, d = cf->menu->items; d->name; d++) {
812 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
813 continue;
814 if (key == d->key)
815 return d;
816 (*idx)++;
817 }
818
819 return NULL;
820 }
821
822 static void ui_draw_menuitem(struct cfdisk *cf,
823 struct cfdisk_menuitem *d,
824 size_t idx)
825 {
826 char buf[80 * MB_CUR_MAX];
827 const char *name;
828 size_t width = cf->menu->width + 2; /* 2 = blank around string */
829 int ln, cl, vert = cf->menu->vertical;
830
831 if (!menuitem_on_page(cf, idx))
832 return; /* no visible item */
833 ln = menuitem_get_line(cf, idx);
834 cl = menuitem_get_column(cf, idx);
835
836 name = _(d->name);
837 mbsalign(name, buf, sizeof(buf), &width,
838 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
839 0);
840
841 DBG(FRONTEND, ul_debug("ui: menuitem: cl=%d, ln=%d, item='%s'",
842 cl, ln, buf));
843
844 if (vert) {
845 mvaddch(ln, cl - 1, ACS_VLINE);
846 mvaddch(ln, cl + cf->menu->width + 4, ACS_VLINE);
847 }
848
849 if (cf->menu->idx == idx) {
850 standout();
851 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
852 standend();
853 if (d->desc)
854 ui_hint(d->desc);
855 } else
856 mvprintw(ln, cl, vert ? " %s " : "[%s]", buf);
857 }
858
859 static void ui_draw_menu(struct cfdisk *cf)
860 {
861 struct cfdisk_menuitem *d;
862 struct cfdisk_menu *m;
863 size_t i = 0;
864 size_t ln = menuitem_get_line(cf, 0);
865 size_t nlines;
866
867 assert(cf);
868 assert(cf->menu);
869
870 DBG(FRONTEND, ul_debug("ui: menu: draw start"));
871
872 m = cf->menu;
873
874 if (m->vertical)
875 nlines = m->page_sz ? m->page_sz : m->nitems;
876 else
877 nlines = menuitem_get_line(cf, m->nitems);
878
879 for (i = ln; i <= ln + nlines; i++) {
880 move(i, 0);
881 clrtoeol();
882 }
883
884 if (m->ignore_cb)
885 menu_update_ignore(cf);
886 i = 0;
887 while ((d = menu_get_menuitem(cf, i)))
888 ui_draw_menuitem(cf, d, i++);
889
890 if (m->vertical) {
891 size_t cl = menuitem_get_column(cf, 0);
892 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
893
894 /* corners and horizontal lines */
895 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
896 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
897
898 for (i = 0; i < m->width + 4; i++) {
899 mvaddch(ln - 1, cl + i, ACS_HLINE);
900 mvaddch(ln + nlines, cl + i, ACS_HLINE);
901 }
902
903 mvaddch(ln - 1, cl + i, ACS_URCORNER);
904 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
905
906 /* draw also lines around empty lines on last page */
907 if (m->page_sz &&
908 m->nitems / m->page_sz == m->idx / m->page_sz) {
909 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
910 mvaddch(i, cl - 1, ACS_VLINE);
911 mvaddch(i, cl + cf->menu->width + 4, ACS_VLINE);
912 }
913 }
914 if (m->title) {
915 attron(A_BOLD);
916 mvprintw(ln - 1, cl, " %s ", m->title);
917 attroff(A_BOLD);
918 }
919 if (curpg != 0)
920 mvaddch(ln - 1, cl + m->width + 3, ACS_UARROW);
921 if (m->page_sz && curpg < m->nitems / m->page_sz)
922 mvaddch(ln + nlines, cl + m->width + 3, ACS_DARROW);
923 }
924
925 DBG(FRONTEND, ul_debug("ui: menu: draw end."));
926 }
927
928 static void ui_menu_goto(struct cfdisk *cf, int where)
929 {
930 struct cfdisk_menuitem *d;
931 size_t old;
932
933 /* stop and begin/end for vertical menus */
934 if (cf->menu->vertical) {
935 if (where < 0)
936 where = 0;
937 else if (where > (int) cf->menu->nitems - 1)
938 where = cf->menu->nitems - 1;
939 } else {
940 /* continue from begin/end */
941 if (where < 0)
942 where = cf->menu->nitems - 1;
943 else if ((size_t) where > cf->menu->nitems - 1)
944 where = 0;
945 }
946 if ((size_t) where == cf->menu->idx)
947 return;
948
949 ui_clean_info();
950
951 old = cf->menu->idx;
952 cf->menu->idx = where;
953
954 if (!menuitem_on_page(cf, old)) {
955 ui_draw_menu(cf);
956 return;
957 }
958
959 d = menu_get_menuitem(cf, old);
960 ui_draw_menuitem(cf, d, old);
961
962 d = menu_get_menuitem(cf, where);
963 ui_draw_menuitem(cf, d, where);
964 }
965
966 static int ui_menu_move(struct cfdisk *cf, int key)
967 {
968 struct cfdisk_menu *m;
969
970 assert(cf);
971 assert(cf->menu);
972
973 m = cf->menu;
974
975 DBG(FRONTEND, ul_debug("ui: menu move key >%c<.", key));
976
977 if (m->vertical)
978 {
979 switch (key) {
980 case KEY_DOWN:
981 case '\016': /* ^N */
982 case 'j': /* Vi-like alternative */
983 ui_menu_goto(cf, m->idx + 1);
984 return 0;
985 case KEY_UP:
986 case '\020': /* ^P */
987 case 'k': /* Vi-like alternative */
988 ui_menu_goto(cf, (int) m->idx - 1);
989 return 0;
990 case KEY_PPAGE:
991 if (m->page_sz) {
992 ui_menu_goto(cf, (int) m->idx - m->page_sz);
993 return 0;
994 }
995 case KEY_HOME:
996 ui_menu_goto(cf, 0);
997 return 0;
998 case KEY_NPAGE:
999 if (m->page_sz) {
1000 ui_menu_goto(cf, m->idx + m->page_sz);
1001 return 0;
1002 }
1003 case KEY_END:
1004 ui_menu_goto(cf, m->nitems);
1005 return 0;
1006 }
1007 } else {
1008 switch (key) {
1009 case KEY_RIGHT:
1010 case '\t':
1011 ui_menu_goto(cf, m->idx + 1);
1012 return 0;
1013 case KEY_LEFT:
1014 #ifdef KEY_BTAB
1015 case KEY_BTAB:
1016 #endif
1017 ui_menu_goto(cf, (int) m->idx - 1);
1018 return 0;
1019 }
1020 }
1021
1022 return 1; /* key irrelevant for menu move */
1023 }
1024
1025 static int partition_on_page(struct cfdisk *cf, size_t i)
1026 {
1027 if (cf->page_sz == 0 ||
1028 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1029 return 1;
1030 return 0;
1031 }
1032
1033 static void ui_draw_partition(struct cfdisk *cf, size_t i)
1034 {
1035 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1036 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
1037 int cur = cf->lines_idx == i;
1038 size_t curpg = 0;
1039
1040 if (cf->page_sz) {
1041 if (!partition_on_page(cf, i))
1042 return;
1043 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1044 curpg = cf->lines_idx / cf->page_sz;
1045 }
1046
1047 DBG(FRONTEND, ul_debug(
1048 "ui: draw partition %zu [page_sz=%zu, "
1049 "line=%d, idx=%zu]",
1050 i, cf->page_sz, ln, cf->lines_idx));
1051
1052 if (cur) {
1053 attron(A_REVERSE);
1054 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1055 mvaddstr(ln, cl, cf->lines[i + 1]);
1056 attroff(A_REVERSE);
1057 } else {
1058 int at = 0;
1059
1060 if (colors_wanted() && is_freespace(cf, i)) {
1061 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1062 at = 1;
1063 }
1064 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1065 mvaddstr(ln, cl, cf->lines[i + 1]);
1066 if (at)
1067 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
1068 }
1069
1070 if ((size_t) ln == MENU_START_LINE - 1 &&
1071 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1072 if (cur)
1073 attron(A_REVERSE);
1074 mvaddch(ln, COLS - 1, ACS_DARROW);
1075 mvaddch(ln, 0, ACS_DARROW);
1076 if (cur)
1077 attroff(A_REVERSE);
1078 }
1079 }
1080
1081 static int ui_draw_table(struct cfdisk *cf)
1082 {
1083 int cl = ARROW_CURSOR_WIDTH;
1084 size_t i, nparts = fdisk_table_get_nents(cf->table);
1085 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
1086
1087 DBG(FRONTEND, ul_debug("ui: draw table"));
1088
1089 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1090 move(i, 0);
1091 clrtoeol();
1092 }
1093
1094 if ((size_t) cf->lines_idx > nparts - 1)
1095 cf->lines_idx = nparts ? nparts - 1 : 0;
1096
1097 /* print header */
1098 attron(A_BOLD);
1099 mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
1100 attroff(A_BOLD);
1101
1102 /* print partitions */
1103 for (i = 0; i < nparts; i++)
1104 ui_draw_partition(cf, i);
1105
1106 if (curpg != 0) {
1107 mvaddch(TABLE_START_LINE, COLS - 1, ACS_UARROW);
1108 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1109 }
1110 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1111 mvaddch(MENU_START_LINE - 1, COLS - 1, ACS_DARROW);
1112 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1113 }
1114 return 0;
1115 }
1116
1117 static int ui_table_goto(struct cfdisk *cf, int where)
1118 {
1119 size_t old;
1120 size_t nparts = fdisk_table_get_nents(cf->table);
1121
1122 DBG(FRONTEND, ul_debug("ui: goto table %d", where));
1123
1124 if (where < 0)
1125 where = 0;
1126 else if ((size_t) where > nparts - 1)
1127 where = nparts - 1;
1128
1129 if ((size_t) where == cf->lines_idx)
1130 return 0;
1131
1132 old = cf->lines_idx;
1133 cf->lines_idx = where;
1134
1135 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1136 ui_draw_table(cf);
1137 else {
1138 ui_draw_partition(cf, old); /* cleanup old */
1139 ui_draw_partition(cf, where); /* draw new */
1140 }
1141 ui_clean_info();
1142 ui_draw_menu(cf);
1143 refresh();
1144 return 0;
1145 }
1146
1147 static int ui_refresh(struct cfdisk *cf)
1148 {
1149 char *id = NULL;
1150 uint64_t bytes = cf->cxt->total_sectors * cf->cxt->sector_size;
1151 char *strsz = size_to_human_string(SIZE_SUFFIX_SPACE
1152 | SIZE_SUFFIX_3LETTER, bytes);
1153 erase();
1154
1155 if (!ui_enabled)
1156 return -EINVAL;
1157
1158 /* header */
1159 attron(A_BOLD);
1160 ui_center(0, _("Disk: %s"), cf->cxt->dev_path);
1161 attroff(A_BOLD);
1162 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
1163 strsz, bytes, (uintmax_t) cf->cxt->total_sectors);
1164 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1165 ui_center(2, _("Label: %s, identifier: %s"),
1166 cf->cxt->label->name, id);
1167 else
1168 ui_center(2, _("Label: %s"), cf->cxt->label->name);
1169 free(strsz);
1170
1171 ui_draw_table(cf);
1172 ui_draw_menu(cf);
1173 refresh();
1174 return 0;
1175 }
1176
1177 static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
1178 const char *hint, char *buf, size_t len)
1179 {
1180 size_t cells = 0;
1181 ssize_t i = 0, rc = -1;
1182 wint_t c;
1183 int ln = MENU_START_LINE, cl = 1;
1184
1185 assert(cf);
1186 assert(buf);
1187 assert(len);
1188
1189 move(ln, 0);
1190 clrtoeol();
1191
1192 if (prompt) {
1193 mvaddstr(ln, cl, (char *) prompt);
1194 cl += mbs_safe_width(prompt);
1195 }
1196
1197 /* default value */
1198 if (*buf) {
1199 i = strlen(buf);
1200 cells = mbs_safe_width(buf);
1201 mvaddstr(ln, cl, buf);
1202 }
1203
1204 if (hint)
1205 ui_hint(hint);
1206 else
1207 ui_clean_hint();
1208
1209 move(ln, cl + cells);
1210 curs_set(1);
1211 refresh();
1212
1213 while (1) {
1214 #if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1215 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1216 if (get_wch(&c) == ERR) {
1217 #else
1218 if ((c = getch()) == ERR) {
1219 #endif
1220 if (!isatty(STDIN_FILENO))
1221 exit(2);
1222 else
1223 goto done;
1224 }
1225 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1226 break;
1227
1228 switch (c) {
1229 case KEY_ESC:
1230 rc = -CFDISK_ERR_ESC;
1231 goto done;
1232 case KEY_DELETE:
1233 case '\b':
1234 case KEY_BACKSPACE:
1235 if (i > 0) {
1236 cells--;
1237 i = mbs_truncate(buf, &cells);
1238 if (i < 0)
1239 goto done;
1240 mvaddch(ln, cl + cells, ' ');
1241 move(ln, cl + cells);
1242 } else
1243 beep();
1244 break;
1245 default:
1246 #if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1247 if (i + 1 < (ssize_t) len && iswprint(c)) {
1248 wchar_t wc = (wchar_t) c;
1249 char s[MB_CUR_MAX + 1];
1250 int sz = wctomb(s, wc);
1251
1252 if (sz > 0 && sz + i < (ssize_t) len) {
1253 s[sz] = '\0';
1254 mvaddnstr(ln, cl + cells, s, sz);
1255 memcpy(buf + i, s, sz);
1256 i += sz;
1257 buf[i] = '\0';
1258 cells += wcwidth(wc);
1259 } else
1260 beep();
1261 }
1262 #else
1263 if (i + 1 < (ssize_t) len && isprint(c)) {
1264 mvaddch(ln, cl + cells, c);
1265 buf[i++] = c;
1266 buf[i] = '\0';
1267 cells++;
1268 }
1269 #endif
1270 else
1271 beep();
1272 }
1273 refresh();
1274 }
1275
1276 rc = i; /* success */
1277 done:
1278 move(ln, 0);
1279 clrtoeol();
1280 curs_set(0);
1281 refresh();
1282
1283 return rc;
1284 }
1285
1286 /* @res is default value as well as result in bytes */
1287 static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
1288 uintmax_t low, uintmax_t up)
1289 {
1290 char buf[128];
1291 uintmax_t user = 0;
1292 ssize_t rc;
1293 char *dflt = size_to_human_string(0, *res);
1294
1295 DBG(FRONTEND, ul_debug("ui: get_size (default=%ju)", *res));
1296
1297 ui_clean_info();
1298
1299 do {
1300 int pwr = 0, insec = 0;
1301
1302 snprintf(buf, sizeof(buf), "%s", dflt);
1303 rc = ui_get_string(cf, prompt,
1304 _("May be followed by {M,B,G,T}iB "
1305 "(the \"iB\" is optional) or S for sectors."),
1306 buf, sizeof(buf));
1307 if (rc == 0) {
1308 ui_warnx(_("Please, specify size."));
1309 continue; /* nothing specified */
1310 } else if (rc == -CFDISK_ERR_ESC)
1311 break; /* cancel dialog */
1312
1313 if (strcmp(buf, dflt) == 0)
1314 user = *res, rc = 0; /* no change, use default */
1315 else {
1316 size_t len = strlen(buf);
1317 if (buf[len - 1] == 'S') {
1318 insec = 1;
1319 buf[len - 1] = '\0';
1320 }
1321 rc = parse_size(buf, &user, &pwr); /* parse */
1322 }
1323
1324 if (rc == 0) {
1325 DBG(FRONTEND, ul_debug("ui: get_size user=%ju, power=%d, sectors=%s",
1326 user, pwr, insec ? "yes" : "no"));
1327 if (insec)
1328 user *= cf->cxt->sector_size;
1329 if (user < low) {
1330 ui_warnx(_("Minimal size is %ju"), low);
1331 rc = -ERANGE;
1332 }
1333 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1334 /* ignore when the user specified size overflow
1335 * with in range specified by suffix (e.g. MiB) */
1336 user = up;
1337
1338 if (user > up) {
1339 ui_warnx(_("Maximal size is %ju bytes."), up);
1340 rc = -ERANGE;
1341 }
1342 } else
1343 ui_warnx(_("Failed to parse size."));
1344 } while (rc != 0);
1345
1346 if (rc == 0)
1347 *res = user;
1348 free(dflt);
1349
1350 DBG(FRONTEND, ul_debug("ui: get_size (result=%ju, rc=%zd)", *res, rc));
1351 return rc;
1352 }
1353
1354 static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1355 struct fdisk_parttype *cur)
1356 {
1357 struct cfdisk_menuitem *d, *cm;
1358 size_t i = 0, nitems, idx = 0;
1359 struct fdisk_parttype *t = NULL;
1360 int has_typestr = 0;
1361
1362 DBG(FRONTEND, ul_debug("ui: asking for parttype."));
1363
1364 /* create cfdisk menu according to label types, note that the
1365 * last cm[] item has to be empty -- so nitems + 1 */
1366 nitems = cf->cxt->label->nparttypes;
1367 if (!nitems)
1368 return NULL;
1369 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
1370 if (!cm)
1371 return NULL;
1372
1373 has_typestr = cf->cxt->label->parttypes[0].typestr &&
1374 *cf->cxt->label->parttypes[0].typestr;
1375
1376 for (i = 0; i < nitems; i++) {
1377 struct fdisk_parttype *x = &cf->cxt->label->parttypes[i];
1378 char *name;
1379
1380 if (!x || !x->name)
1381 continue;
1382 cm[i].userdata = x;
1383 if (!has_typestr)
1384 xasprintf(&name, "%2x %s", x->type, x->name);
1385 else {
1386 name = (char *) x->name;
1387 cm[i].desc = x->typestr;
1388 }
1389 cm[i].name = name;
1390 if (x == cur)
1391 idx = i;
1392 }
1393
1394 /* make the new menu active */
1395 menu_push(cf, cm);
1396 cf->menu->vertical = 1;
1397 cf->menu->idx = idx;
1398 menu_set_title(cf->menu, _("Select partition type"));
1399 ui_draw_menu(cf);
1400 refresh();
1401
1402 do {
1403 int key = getch();
1404
1405 if (ui_menu_move(cf, key) == 0)
1406 continue;
1407
1408 switch (key) {
1409 case KEY_ENTER:
1410 case '\n':
1411 case '\r':
1412 d = menu_get_menuitem(cf, cf->menu->idx);
1413 if (d)
1414 t = (struct fdisk_parttype *) d->userdata;
1415 goto done;
1416 case 'q':
1417 case 'Q':
1418 goto done;
1419 }
1420 } while (1);
1421
1422 done:
1423 menu_pop(cf);
1424 if (!has_typestr) {
1425 for (i = 0; i < nitems; i++)
1426 free((char *) cm[i].name);
1427 }
1428 free(cm);
1429 DBG(FRONTEND, ul_debug("ui: get parrtype done [type=%s] ", t ? t->name : NULL));
1430 return t;
1431 }
1432
1433 /* prints menu with libfdisk labels and waits for users response */
1434 static int ui_create_label(struct cfdisk *cf)
1435 {
1436 struct cfdisk_menuitem *d, *cm;
1437 int rc = 1;
1438 size_t i = 0, nitems;
1439 struct fdisk_label *lb = NULL;
1440
1441 assert(cf);
1442
1443 DBG(FRONTEND, ul_debug("ui: asking for new disklabe."));
1444
1445 /* create cfdisk menu according to libfdisk labels, note that the
1446 * last cm[] item has to be empty -- so nitems + 1 */
1447 nitems = fdisk_context_get_nlabels(cf->cxt);
1448 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
1449
1450 for (i = 0; i < nitems; i++) {
1451 if (fdisk_context_next_label(cf->cxt, &lb))
1452 break;
1453 cm[i].name = lb->name;
1454 }
1455
1456 erase();
1457 ui_center(LINES - 4,
1458 _("Device does not contain a recognized partition table."));
1459 ui_center(LINES - 3,
1460 _("Please, select a type to create a new disk label."));
1461
1462 /* make the new menu active */
1463 menu_push(cf, cm);
1464 cf->menu->vertical = 1;
1465 menu_set_title(cf->menu, _("Select label type"));
1466 ui_draw_menu(cf);
1467 refresh();
1468
1469 do {
1470 int key = getch();
1471 if (ui_menu_move(cf, key) == 0)
1472 continue;
1473 switch (key) {
1474 case KEY_ENTER:
1475 case '\n':
1476 case '\r':
1477 d = menu_get_menuitem(cf, cf->menu->idx);
1478 if (d)
1479 rc = fdisk_create_disklabel(cf->cxt, d->name);
1480 goto done;
1481 case 'q':
1482 case 'Q':
1483 goto done;
1484 }
1485 } while (1);
1486
1487 done:
1488 menu_pop(cf);
1489 free(cm);
1490 DBG(FRONTEND, ul_debug("ui: create label done [rc=%d] ", rc));
1491 return rc;
1492 }
1493
1494 static int ui_help(void)
1495 {
1496 size_t i;
1497 static const char *help[] = {
1498 N_("Help Screen for cfdisk"),
1499 "",
1500 N_("This is cfdisk, a curses based disk partitioning program, which"),
1501 N_("allows you to create, delete and modify partitions on your hard"),
1502 N_("disk drive."),
1503 "",
1504 N_("Copyright (C) 2014 Karel Zak <kzak@redhat.com> "),
1505 N_("Based on the original cfdisk from Kevin E. Martin & aeb."),
1506 "",
1507 N_("Command Meaning"),
1508 N_("------- -------"),
1509 N_(" b Toggle bootable flag of the current partition"),
1510 N_(" d Delete the current partition"),
1511 N_(" h Print this screen"),
1512 N_(" n Create new partition from free space"),
1513 N_(" q Quit program without writing partition table"),
1514 N_(" t Change the partition type"),
1515 N_(" W Write partition table to disk (must enter upper case W)"),
1516 N_(" Since this might destroy data on the disk, you must"),
1517 N_(" either confirm or deny the write by entering `yes' or"),
1518 N_(" `no'"),
1519 N_("Up Arrow Move cursor to the previous partition"),
1520 N_("Down Arrow Move cursor to the next partition"),
1521 N_("Left Arrow Move cursor to the previous menu item"),
1522 N_("Right Arrow Move cursor to the next menu item"),
1523
1524 "",
1525 N_("Note: All of the commands can be entered with either upper or lower"),
1526 N_("case letters (except for Writes)."),
1527 "",
1528 N_("Use lsblk(8) or partx(8) to see more details about the device.")
1529 };
1530
1531 erase();
1532 for (i = 0; i < ARRAY_SIZE(help); i++)
1533 mvaddstr(i, 1, _(help[i]));
1534
1535 ui_info(_("Press a key to continue."));
1536 getch();
1537 return 0;
1538 }
1539
1540 /* TODO: use @sz, now 128bytes */
1541 static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
1542 size_t sz __attribute__((__unused__)))
1543 {
1544 struct fdisk_partition *pa = get_current_partition(cf);
1545 size_t i = 0;
1546
1547 if (!pa)
1548 return 0;
1549 if (fdisk_partition_is_freespace(pa)) {
1550 ignore[i++] = 'd'; /* delete */
1551 ignore[i++] = 't'; /* set type */
1552 ignore[i++] = 'b'; /* set bootable */
1553 } else {
1554 ignore[i++] = 'n';
1555 if (!fdisk_is_disklabel(cf->cxt, DOS) &&
1556 !fdisk_is_disklabel(cf->cxt, SGI))
1557 ignore[i++] = 'b';
1558 }
1559
1560
1561 if (fdisk_context_is_readonly(cf->cxt))
1562 ignore[i++] = 'W';
1563 return i;
1564 }
1565
1566
1567 /* returns: error: < 0, success: 0, quit: 1 */
1568 static int main_menu_action(struct cfdisk *cf, int key)
1569 {
1570 size_t n;
1571 int ref = 0, rc;
1572 const char *info = NULL, *warn = NULL;
1573 struct fdisk_partition *pa;
1574
1575 assert(cf);
1576 assert(cf->cxt);
1577 assert(cf->menu);
1578
1579 if (key == 0) {
1580 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
1581 if (!d)
1582 return 0;
1583 key = d->key;
1584
1585 } else if (key != 'w' && key != 'W')
1586 key = tolower(key); /* case insensitive except 'W'rite */
1587
1588 DBG(FRONTEND, ul_debug("ui: main menu action: key=%c", key));
1589
1590 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
1591 DBG(FRONTEND, ul_debug(" ignore '%c'", key));
1592 return 0;
1593 }
1594
1595 pa = get_current_partition(cf);
1596 n = fdisk_partition_get_partno(pa);
1597
1598 DBG(FRONTEND, ul_debug("menu action on %p", pa));
1599 ui_clean_hint();
1600 ui_clean_info();
1601
1602 switch (key) {
1603 case 'b': /* Bootable flag */
1604 {
1605 int fl = fdisk_is_disklabel(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
1606 fdisk_is_disklabel(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
1607
1608 if (fl && fdisk_partition_toggle_flag(cf->cxt, n, fl))
1609 warn = _("Could not toggle the flag.");
1610 else if (fl)
1611 ref = 1;
1612 break;
1613 }
1614 #ifdef KEY_DC
1615 case KEY_DC:
1616 #endif
1617 case 'd': /* Delete */
1618 if (fdisk_delete_partition(cf->cxt, n) != 0)
1619 warn = _("Could not delete partition %zu.");
1620 else
1621 info = _("Partition %zu has been deleted.");
1622 ref = 1;
1623 break;
1624 case 'h': /* help */
1625 ui_help();
1626 ref = 1;
1627 break;
1628 case 'n': /* New */
1629 {
1630 uint64_t start, size, dflt_size;
1631 struct fdisk_partition *npa; /* the new partition */
1632
1633 if (!pa || !fdisk_partition_is_freespace(pa))
1634 return -EINVAL;
1635 npa = fdisk_new_partition();
1636 if (!npa)
1637 return -ENOMEM;
1638 /* free space range */
1639 start = fdisk_partition_get_start(pa);
1640 size = dflt_size = fdisk_partition_get_size(pa) * cf->cxt->sector_size;
1641
1642 if (ui_get_size(cf, _("Partition size: "), &size, 1, size)
1643 == -CFDISK_ERR_ESC)
1644 break;
1645
1646 if (dflt_size == size) /* default is to fillin all free space */
1647 fdisk_partition_end_follow_default(npa, 1);
1648 else /* set relative size of the partition */
1649 fdisk_partition_set_size(npa, size / cf->cxt->sector_size);
1650
1651 fdisk_partition_set_start(npa, start);
1652 fdisk_partition_partno_follow_default(npa, 1);
1653 /* add to disk label -- libfdisk will ask for missing details */
1654 rc = fdisk_add_partition(cf->cxt, npa);
1655 fdisk_unref_partition(npa);
1656 if (rc == 0)
1657 ref = 1;
1658 break;
1659 }
1660 case 'q': /* Quit */
1661 return 1;
1662 case 't': /* Type */
1663 {
1664 struct fdisk_parttype *t;
1665
1666 if (!pa || fdisk_partition_is_freespace(pa))
1667 return -EINVAL;
1668 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
1669 t = ui_get_parttype(cf, t);
1670 ref = 1;
1671
1672 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
1673 info = _("Changed type of the partition %zu.");
1674 else
1675 info = _("Type of the partition %zu is unchanged.");
1676 break;
1677 }
1678 case 'W': /* Write */
1679 {
1680 char buf[64] = { 0 };
1681 int rc;
1682
1683 if (fdisk_context_is_readonly(cf->cxt)) {
1684 ui_warnx(_("Device open in read-only mode"));
1685 break;
1686 }
1687
1688 rc = ui_get_string(cf,
1689 _("Are you sure you want to write the partition "
1690 "table to disk? "),
1691 _("Type \"yes\" or \"no\" or press ESC to left dialog."),
1692 buf, sizeof(buf));
1693
1694 ref = 1;
1695 if (rc <= 0 || strcasecmp(buf, "yes") != 0
1696 || strcasecmp(buf, _("yes")) != 0) {
1697 info = _("Did not write partition table to disk");
1698 break;
1699 }
1700 rc = fdisk_write_disklabel(cf->cxt);
1701 if (rc)
1702 warn = _("Failed to write disklabel");
1703 else {
1704 fdisk_reread_partition_table(cf->cxt);
1705 info = _("The partition table has been altered.");
1706 }
1707 break;
1708 }
1709 default:
1710 break;
1711 }
1712
1713 if (ref) {
1714 lines_refresh(cf);
1715 ui_refresh(cf);
1716 }
1717
1718 ui_clean_hint();
1719 if (warn)
1720 ui_warnx(warn, n + 1);
1721 else if (info)
1722 ui_info(info, n + 1);
1723
1724 return 0;
1725 }
1726
1727 static int ui_run(struct cfdisk *cf)
1728 {
1729 int rc = 0;
1730
1731 DBG(FRONTEND, ul_debug("ui: start COLS=%d, LINES=%d", COLS, LINES));
1732
1733 if (!fdisk_dev_has_disklabel(cf->cxt)) {
1734 rc = ui_create_label(cf);
1735 if (rc < 0)
1736 ui_errx(EXIT_FAILURE,
1737 _("failed to create a new disklabel"));
1738 if (rc)
1739 return rc;
1740 }
1741
1742 cols_init(cf);
1743 rc = lines_refresh(cf);
1744 if (rc)
1745 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
1746
1747 menu_push(cf, main_menuitems);
1748 cf->menu->ignore_cb = main_menu_ignore_keys;
1749
1750 rc = ui_refresh(cf);
1751 if (rc)
1752 return rc;
1753
1754 if (fdisk_context_is_readonly(cf->cxt))
1755 ui_warnx(_("Device open in read-only mode."));
1756
1757 do {
1758 int rc = 0, key = getch();
1759
1760 if (ui_menu_move(cf, key) == 0)
1761 continue;
1762
1763 DBG(FRONTEND, ul_debug("ui: main action key >%c<.", key));
1764
1765 switch (key) {
1766 case KEY_DOWN:
1767 case '\016': /* ^N */
1768 case 'j': /* Vi-like alternative */
1769 ui_table_goto(cf, cf->lines_idx + 1);
1770 break;
1771 case KEY_UP:
1772 case '\020': /* ^P */
1773 case 'k': /* Vi-like alternative */
1774 ui_table_goto(cf, (int) cf->lines_idx - 1);
1775 break;
1776 case KEY_PPAGE:
1777 if (cf->page_sz) {
1778 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
1779 break;
1780 }
1781 case KEY_HOME:
1782 ui_table_goto(cf, 0);
1783 break;
1784 case KEY_NPAGE:
1785 if (cf->page_sz) {
1786 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
1787 break;
1788 }
1789 case KEY_END:
1790 ui_table_goto(cf, (int) cf->nlines - 1);
1791 break;
1792 case KEY_ENTER:
1793 case '\n':
1794 case '\r':
1795 rc = main_menu_action(cf, 0);
1796 break;
1797 default:
1798 rc = main_menu_action(cf, key);
1799 if (rc < 0)
1800 beep();
1801 break;
1802 }
1803
1804 if (rc == 1)
1805 break; /* quit */
1806 } while (1);
1807
1808 menu_pop(cf);
1809
1810 DBG(FRONTEND, ul_debug("ui: end"));
1811
1812 return 0;
1813 }
1814
1815 static void __attribute__ ((__noreturn__)) usage(FILE *out)
1816 {
1817 fputs(USAGE_HEADER, out);
1818
1819 fprintf(out,
1820 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
1821
1822 fputs(USAGE_OPTIONS, out);
1823 fputs(_(" -L --color[=<when>] colorize output (auto, always or never)\n"), out);
1824
1825 fputs(USAGE_SEPARATOR, out);
1826 fputs(USAGE_HELP, out);
1827 fputs(USAGE_VERSION, out);
1828
1829 fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
1830 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
1831 }
1832
1833 int main(int argc, char *argv[])
1834 {
1835 int rc, c, colormode = UL_COLORMODE_UNDEF;
1836 struct cfdisk _cf = { .lines_idx = 0 },
1837 *cf = &_cf;
1838
1839 static const struct option longopts[] = {
1840 { "color", optional_argument, NULL, 'L' },
1841 { "help", no_argument, NULL, 'h' },
1842 { "version", no_argument, NULL, 'V' },
1843 { NULL, 0, 0, 0 },
1844 };
1845
1846 setlocale(LC_ALL, "");
1847 bindtextdomain(PACKAGE, LOCALEDIR);
1848 textdomain(PACKAGE);
1849 atexit(close_stdout);
1850
1851 while((c = getopt_long(argc, argv, "L::hV", longopts, NULL)) != -1) {
1852 switch(c) {
1853 case 'h':
1854 usage(stdout);
1855 break;
1856 case 'L':
1857 colormode = UL_COLORMODE_AUTO;
1858 if (optarg)
1859 colormode = colormode_or_err(optarg,
1860 _("unsupported color mode"));
1861 break;
1862 case 'V':
1863 printf(_("%s from %s\n"), program_invocation_short_name,
1864 PACKAGE_STRING);
1865 return EXIT_SUCCESS;
1866 }
1867 }
1868
1869
1870
1871 colors_init(colormode, "cfdisk");
1872
1873 fdisk_init_debug(0);
1874 cf->cxt = fdisk_new_context();
1875 if (!cf->cxt)
1876 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
1877
1878 fdisk_context_set_ask(cf->cxt, ask_callback, (void *) cf);
1879
1880 if (optind == argc)
1881 usage(stderr);
1882
1883 rc = fdisk_context_assign_device(cf->cxt, argv[optind], 0);
1884 if (rc == -EACCES)
1885 rc = fdisk_context_assign_device(cf->cxt, argv[optind], 1);
1886 if (rc != 0)
1887 err(EXIT_FAILURE, _("cannot open %s"), argv[optind]);
1888
1889 /* Don't use err(), warn() from this point */
1890 ui_init(cf);
1891 ui_run(cf);
1892 ui_end();
1893
1894 free(cf->lines);
1895 free(cf->linesbuf);
1896 fdisk_unref_table(cf->table);
1897
1898 rc = fdisk_context_deassign_device(cf->cxt);
1899 fdisk_free_context(cf->cxt);
1900 DBG(FRONTEND, ul_debug("bye! [rc=%d]", rc));
1901 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
1902 }