9 #elif defined(HAVE_SLANG_SLANG_H)
10 #include <slang/slang.h>
13 #ifdef HAVE_SLCURSES_H
15 #elif defined(HAVE_SLANG_SLCURSES_H)
16 #include <slang/slcurses.h>
17 #elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
18 #include <ncursesw/ncurses.h>
19 #elif defined(HAVE_NCURSES_H)
21 #elif defined(HAVE_NCURSES_NCURSES_H)
22 #include <ncurses/ncurses.h>
30 #include "closestream.h"
38 #define ARROW_CURSOR_STRING ">>> "
39 #define ARROW_CURSOR_DUMMY " "
40 #define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
42 #define MENU_PADDING 2
43 #define TABLE_START_LINE 4
44 #define MENU_START_LINE (LINES - 5)
45 #define INFO_LINE (LINES - 2)
46 #define HINT_LINE (LINES - 1)
48 #define CFDISK_ERR_ESC 5000
51 # define KEY_ESC '\033'
54 # define KEY_DELETE '\177'
63 static const int color_pairs
[][2] = {
64 /* color foreground, background */
65 [CFDISK_CL_WARNING
] = { COLOR_RED
, -1 },
66 [CFDISK_CL_FREESPACE
] = { COLOR_GREEN
, -1 }
71 static struct cfdisk_menudesc
*menu_get_menuitem(struct cfdisk
*cf
, size_t idx
);
72 static struct cfdisk_menudesc
*menu_get_menuitem_by_key(struct cfdisk
*cf
, int key
, size_t *idx
);
73 static struct cfdisk_menu
*menu_push(struct cfdisk
*cf
, struct cfdisk_menudesc
*desc
);
74 static struct cfdisk_menu
*menu_pop(struct cfdisk
*cf
);
76 static int ui_refresh(struct cfdisk
*cf
);
77 static void ui_warnx(const char *fmt
, ...);
78 static void ui_warn(const char *fmt
, ...);
79 static void ui_info(const char *fmt
, ...);
80 static void ui_draw_menu(struct cfdisk
*cf
);
81 static int ui_menu_move(struct cfdisk
*cf
, int key
);
83 static int ui_get_size(struct cfdisk
*cf
, const char *prompt
, uintmax_t *res
,
84 uintmax_t low
, uintmax_t up
);
86 static int ui_enabled
;
88 struct cfdisk_menudesc
{
89 int key
; /* keyboard shortcut */
90 const char *name
; /* item name */
91 const char *desc
; /* item description */
97 struct cfdisk_menudesc
*desc
;
105 struct cfdisk_menu
*prev
;
106 int (*ignore_cb
) (struct cfdisk
*, char *, size_t);
108 unsigned int vertical
: 1;
111 static struct cfdisk_menudesc main_menudesc
[] = {
112 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
113 { 'd', N_("Delete"), N_("Delete the current partition") },
114 // { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
115 { 'n', N_("New"), N_("Create new partition from free space") },
116 // { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
117 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
118 { 't', N_("Type"), N_("Change the partition type") },
119 { 'h', N_("Help"), N_("Print help screen") },
120 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
125 struct fdisk_context
*cxt
; /* libfdisk context */
126 struct fdisk_table
*table
; /* partition table */
128 struct cfdisk_menu
*menu
; /* the current menu */
130 int *cols
; /* output columns */
131 size_t ncols
; /* number of columns */
133 char *linesbuf
; /* table as string */
134 size_t linesbufsz
; /* size of the tb_buf */
136 char **lines
; /* array with lines */
137 size_t nlines
; /* number of lines */
138 size_t lines_idx
; /* current line <0..N>, exclude header */
141 static int cols_init(struct cfdisk
*cf
)
149 return fdisk_get_columns(cf
->cxt
, 0, &cf
->cols
, &cf
->ncols
);
152 /* It would be possible to use fdisk_table_to_string(), but we want some
153 * extension to the output format, so let's do it without libfdisk
155 static char *table_to_string(struct cfdisk
*cf
, struct fdisk_table
*tb
)
157 const struct fdisk_column
*col
;
158 struct fdisk_partition
*pa
;
159 struct fdisk_label
*lb
;
160 struct fdisk_iter
*itr
= NULL
;
161 struct tt
*tt
= NULL
;
165 struct tt_line
*ln
, *ln_cont
= NULL
;
167 DBG(FRONTEND
, dbgprint("table: convert to string"));
174 lb
= fdisk_context_get_label(cf
->cxt
, NULL
);
177 itr
= fdisk_new_iter(FDISK_ITER_FORWARD
);
181 /* get container (e.g. extended partition) */
182 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0) {
183 if (fdisk_partition_is_nested(pa
)) {
184 DBG(FRONTEND
, dbgprint("table: nested detected, using tree"));
190 tt
= tt_new_table(TT_FL_FREEDATA
| TT_FL_MAX
| tree
);
195 for (i
= 0; i
< cf
->ncols
; i
++) {
196 col
= fdisk_label_get_column(lb
, cf
->cols
[i
]);
198 int fl
= col
->tt_flags
;
199 if (tree
&& col
->id
== FDISK_COL_DEVICE
)
201 tt_define_column(tt
, col
->name
, col
->width
, fl
);
206 fdisk_reset_iter(itr
, FDISK_ITER_FORWARD
);
208 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0) {
209 struct tt_line
*parent
= fdisk_partition_is_nested(pa
) ? ln_cont
: NULL
;
211 ln
= tt_add_line(tt
, parent
);
214 for (i
= 0; i
< cf
->ncols
; i
++) {
216 col
= fdisk_label_get_column(lb
, cf
->cols
[i
]);
219 if (fdisk_partition_to_string(pa
, cf
->cxt
, col
->id
, &cdata
))
221 tt_line_set_data(ln
, i
, cdata
);
223 if (tree
&& fdisk_partition_is_container(pa
))
226 tt_line_set_userdata(ln
, (void *) pa
);
227 fdisk_ref_partition(pa
);
233 tt_set_termreduce(tt
, ARROW_CURSOR_WIDTH
);
234 tt_print_table_to_string(tt
, &res
);
236 /* tt_* code might to reorder lines, let's reorder @tb according to the
237 * final output (it's no problem because partitions are addressed by
238 * parno stored within struct fdisk_partition) */
241 fdisk_reset_iter(itr
, FDISK_ITER_FORWARD
);
242 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0)
243 fdisk_table_remove_partition(tb
, pa
);
245 /* add all in the right order */
247 while (tt_get_output_line(tt
, i
++, &ln
) == 0) {
248 struct fdisk_partition
*pa
= tt_line_get_userdata(ln
);
250 fdisk_table_add_partition(tb
, pa
);
251 fdisk_unref_partition(pa
);
255 fdisk_free_iter(itr
);
260 static int lines_refresh(struct cfdisk
*cf
)
268 DBG(FRONTEND
, dbgprint("refreshing buffer"));
277 fdisk_unref_table(cf
->table
);
280 /* read partitions and free spaces into cf->table */
281 rc
= fdisk_get_partitions(cf
->cxt
, &cf
->table
);
283 rc
= fdisk_get_freespaces(cf
->cxt
, &cf
->table
);
287 cf
->linesbuf
= table_to_string(cf
, cf
->table
);
291 cf
->linesbufsz
= strlen(cf
->linesbuf
);
292 cf
->nlines
= fdisk_table_get_nents(cf
->table
) + 1; /* 1 for header line */
294 cf
->lines
= xcalloc(cf
->nlines
, sizeof(char *));
296 for (p
= cf
->linesbuf
, i
= 0; p
&& i
< cf
->nlines
; i
++) {
308 static struct fdisk_partition
*get_current_partition(struct cfdisk
*cf
)
313 return fdisk_table_get_partition(cf
->table
, cf
->lines_idx
);
316 static int is_freespace(struct cfdisk
*cf
, size_t i
)
318 struct fdisk_partition
*pa
;
323 pa
= fdisk_table_get_partition(cf
->table
, i
);
324 return fdisk_partition_is_freespace(pa
);
327 /* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
328 * responseback to libfdisk
330 static int ask_menu(struct fdisk_ask
*ask
, struct cfdisk
*cf
)
332 struct cfdisk_menudesc
*d
, *cm
;
334 size_t i
= 0, nitems
;
335 const char *name
, *desc
;
340 /* create cfdisk menu according to libfdisk ask-menu, note that the
341 * last cm[] item has to be empty -- so nitems + 1 */
342 nitems
= fdisk_ask_menu_get_nitems(ask
);
343 cm
= xcalloc(nitems
+ 1, sizeof(struct cfdisk_menudesc
));
345 for (i
= 0; i
< nitems
; i
++) {
346 if (fdisk_ask_menu_get_item(ask
, i
, &key
, &name
, &desc
))
353 /* make the new menu active */
362 if (ui_menu_move(cf
, key
) == 0)
369 d
= menu_get_menuitem(cf
, cf
->menu
->idx
);
371 fdisk_ask_menu_set_result(ask
, d
->key
);
384 static int ask_callback(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
385 void *data
__attribute__((__unused__
)))
392 switch(fdisk_ask_get_type(ask
)) {
393 case FDISK_ASKTYPE_INFO
:
394 ui_info(fdisk_ask_print_get_mesg(ask
));
396 case FDISK_ASKTYPE_WARNX
:
397 ui_warnx(fdisk_ask_print_get_mesg(ask
));
399 case FDISK_ASKTYPE_WARN
:
400 ui_warn(fdisk_ask_print_get_mesg(ask
));
402 case FDISK_ASKTYPE_MENU
:
403 ask_menu(ask
, (struct cfdisk
*) data
);
406 ui_warnx(_("internal error: unsupported dialog type %d"),
407 fdisk_ask_get_type(ask
));
413 static int ui_end(void)
418 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
419 SLsmg_gotorc(LINES
- 1, 0);
422 mvcur(0, COLS
- 1, LINES
-1, 0);
431 static void ui_vprint_center(int line
, int attrs
, const char *fmt
, va_list ap
)
439 xvasprintf(&buf
, fmt
, ap
);
441 width
= mbs_safe_width(buf
);
442 if (width
> (size_t) COLS
) {
443 char *p
= strrchr(buf
+ COLS
, ' ');
447 if (line
+ 1 >= LINES
)
450 mvaddstr(line
, 0, buf
);
451 mvaddstr(line
+ 1, 0, p
+1);
455 mvaddstr(line
, (COLS
- width
) / 2, buf
);
461 static void ui_center(int line
, const char *fmt
, ...)
465 ui_vprint_center(line
, 0, fmt
, ap
);
469 static void ui_warnx(const char *fmt
, ...)
474 ui_vprint_center(INFO_LINE
, COLOR_PAIR(CFDISK_CL_WARNING
), fmt
, ap
);
476 vfprintf(stderr
, fmt
, ap
);
480 static void ui_warn(const char *fmt
, ...)
485 xasprintf(&fmt_m
, "%s: %m", fmt
);
489 ui_vprint_center(INFO_LINE
, COLOR_PAIR(CFDISK_CL_WARNING
), fmt_m
, ap
);
491 vfprintf(stderr
, fmt_m
, ap
);
496 static int __attribute__((__noreturn__
)) ui_errx(int rc
, const char *fmt
, ...)
502 fprintf(stderr
, "%s: ", program_invocation_short_name
);
503 vfprintf(stderr
, fmt
, ap
);
509 static void ui_info(const char *fmt
, ...)
514 ui_vprint_center(INFO_LINE
, A_BOLD
, fmt
, ap
);
516 vfprintf(stdout
, fmt
, ap
);
520 static void ui_clean_info(void)
526 static void ui_hint(const char *fmt
, ...)
531 ui_vprint_center(HINT_LINE
, A_BOLD
, fmt
, ap
);
533 vfprintf(stdout
, fmt
, ap
);
537 static void ui_clean_hint(void)
543 static void die_on_signal(int dummy
__attribute__((__unused__
)))
545 DBG(FRONTEND
, dbgprint("die on signal."));
550 static void menu_update_ignore(struct cfdisk
*cf
)
552 char ignore
[128] = { 0 };
554 struct cfdisk_menu
*m
;
555 struct cfdisk_menudesc
*d
, *org
;
560 assert(cf
->menu
->ignore_cb
);
563 org
= menu_get_menuitem(cf
, m
->idx
);
565 DBG(FRONTEND
, dbgprint("menu: update menu ignored keys"));
567 i
= m
->ignore_cb(cf
, ignore
, sizeof(ignore
));
570 /* return if no change */
571 if ( (!m
->ignore
&& !*ignore
)
572 || (m
->ignore
&& *ignore
&& strcmp(m
->ignore
, ignore
) == 0)) {
577 m
->ignore
= xstrdup(ignore
);
580 for (d
= m
->desc
; d
->name
; d
++) {
581 if (m
->ignore
&& strchr(m
->ignore
, d
->key
))
586 /* refresh menu index to be at the same menuitem or go to the first */
587 if (org
&& menu_get_menuitem_by_key(cf
, org
->key
, &idx
))
592 m
->page_sz
= m
->nitems
/ (LINES
- 4) ? LINES
- 4 : 0;
595 static struct cfdisk_menu
*menu_push(
597 struct cfdisk_menudesc
*desc
)
599 struct cfdisk_menu
*m
= xcalloc(1, sizeof(*m
));
600 struct cfdisk_menudesc
*d
;
604 DBG(FRONTEND
, dbgprint("menu: new menu"));
609 for (d
= m
->desc
; d
->name
; d
++) {
610 const char *name
= _(d
->name
);
611 size_t len
= mbs_safe_width(name
);
618 m
->page_sz
= m
->nitems
/ (LINES
- 4) ? LINES
- 4 : 0;
622 static struct cfdisk_menu
*menu_pop(struct cfdisk
*cf
)
624 struct cfdisk_menu
*m
= NULL
;
628 DBG(FRONTEND
, dbgprint("menu: rem menu"));
632 free(cf
->menu
->ignore
);
633 free(cf
->menu
->title
);
640 static void menu_set_title(struct cfdisk_menu
*m
, const char *title
)
645 size_t len
= mbs_safe_width(title
);
646 if (len
+ 3 > m
->width
)
648 str
= xstrdup(title
);
654 static int ui_init(struct cfdisk
*cf
__attribute__((__unused__
)))
658 DBG(FRONTEND
, dbgprint("ui: init"));
660 /* setup SIGCHLD handler */
661 sigemptyset(&sa
.sa_mask
);
663 sa
.sa_handler
= die_on_signal
;
664 sigaction(SIGINT
, &sa
, NULL
);
665 sigaction(SIGTERM
, &sa
, NULL
);
674 use_default_colors();
676 for (i
= 1; i
< ARRAY_SIZE(color_pairs
); i
++) /* yeah, start from 1! */
677 init_pair(i
, color_pairs
[i
][0], color_pairs
[i
][1]);
684 keypad(stdscr
, TRUE
);
689 static size_t menuitem_get_line(struct cfdisk
*cf
, size_t idx
)
691 struct cfdisk_menu
*m
= cf
->menu
;
694 if (!m
->page_sz
) /* small menu */
695 return (LINES
- (cf
->menu
->nitems
+ 1)) / 2 + idx
;
696 return (idx
% m
->page_sz
) + 1;
698 size_t len
= m
->width
+ 4 + MENU_PADDING
; /* item width */
699 size_t items
= COLS
/ len
; /* items per line */
701 return MENU_START_LINE
+ ((idx
/ items
));
705 static int menuitem_get_column(struct cfdisk
*cf
, size_t idx
)
707 if (cf
->menu
->vertical
) {
708 size_t nc
= cf
->menu
->width
+ MENU_PADDING
;
709 if ((size_t) COLS
<= nc
)
711 return (COLS
- nc
) / 2;
713 size_t len
= cf
->menu
->width
+ 4 + MENU_PADDING
; /* item width */
714 size_t items
= COLS
/ len
; /* items per line */
715 size_t extra
= items
< cf
->menu
->nitems
? /* extra space on line */
716 COLS
% len
: /* - multi-line menu */
717 COLS
- (cf
->menu
->nitems
* len
); /* - one line menu */
719 extra
+= MENU_PADDING
; /* add padding after last item to extra */
722 return (idx
* len
) + (extra
/ 2);
723 return ((idx
% items
) * len
) + (extra
/ 2);
727 static int menuitem_on_page(struct cfdisk
*cf
, size_t idx
)
729 struct cfdisk_menu
*m
= cf
->menu
;
731 if (m
->page_sz
== 0 ||
732 m
->idx
/ m
->page_sz
== idx
/ m
->page_sz
)
737 static struct cfdisk_menudesc
*menu_get_menuitem(struct cfdisk
*cf
, size_t idx
)
739 struct cfdisk_menudesc
*d
;
742 for (i
= 0, d
= cf
->menu
->desc
; d
->name
; d
++) {
743 if (cf
->menu
->ignore
&& strchr(cf
->menu
->ignore
, d
->key
))
752 static struct cfdisk_menudesc
*menu_get_menuitem_by_key(struct cfdisk
*cf
,
753 int key
, size_t *idx
)
755 struct cfdisk_menudesc
*d
;
757 for (*idx
= 0, d
= cf
->menu
->desc
; d
->name
; d
++) {
758 if (cf
->menu
->ignore
&& strchr(cf
->menu
->ignore
, d
->key
))
768 static void ui_draw_menuitem(struct cfdisk
*cf
,
769 struct cfdisk_menudesc
*d
,
772 char buf
[80 * MB_CUR_MAX
];
774 size_t width
= cf
->menu
->width
+ 2; /* 2 = blank around string */
775 int ln
, cl
, vert
= cf
->menu
->vertical
;
777 if (!menuitem_on_page(cf
, idx
))
778 return; /* no visible item */
779 ln
= menuitem_get_line(cf
, idx
);
780 cl
= menuitem_get_column(cf
, idx
);
783 mbsalign(name
, buf
, sizeof(buf
), &width
,
784 vert
? MBS_ALIGN_LEFT
: MBS_ALIGN_CENTER
,
787 DBG(FRONTEND
, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
791 mvaddch(ln
, cl
- 1, ACS_VLINE
);
792 mvaddch(ln
, cl
+ cf
->menu
->width
+ 4, ACS_VLINE
);
795 if (cf
->menu
->idx
== idx
) {
797 mvprintw(ln
, cl
, vert
? " %s " : "[%s]", buf
);
802 mvprintw(ln
, cl
, vert
? " %s " : "[%s]", buf
);
805 static void ui_draw_menu(struct cfdisk
*cf
)
807 struct cfdisk_menudesc
*d
;
808 struct cfdisk_menu
*m
;
810 size_t ln
= menuitem_get_line(cf
, 0);
816 DBG(FRONTEND
, dbgprint("ui: menu: draw start"));
821 nlines
= m
->page_sz
? m
->page_sz
: m
->nitems
;
823 nlines
= menuitem_get_line(cf
, m
->nitems
);
825 for (i
= ln
; i
<= ln
+ nlines
; i
++) {
831 menu_update_ignore(cf
);
833 while ((d
= menu_get_menuitem(cf
, i
)))
834 ui_draw_menuitem(cf
, d
, i
++);
837 size_t cl
= menuitem_get_column(cf
, 0);
838 size_t curpg
= m
->page_sz
? m
->idx
/ m
->page_sz
: 0;
840 /* corners and horizontal lines */
841 mvaddch(ln
- 1, cl
- 1, ACS_ULCORNER
);
842 mvaddch(ln
+ nlines
, cl
- 1, ACS_LLCORNER
);
844 for (i
= 0; i
< m
->width
+ 4; i
++) {
845 mvaddch(ln
- 1, cl
+ i
, ACS_HLINE
);
846 mvaddch(ln
+ nlines
, cl
+ i
, ACS_HLINE
);
849 mvaddch(ln
- 1, cl
+ i
, ACS_URCORNER
);
850 mvaddch(ln
+ nlines
, cl
+ i
, ACS_LRCORNER
);
852 /* draw also lines around empty lines on last page */
854 m
->nitems
/ m
->page_sz
== m
->idx
/ m
->page_sz
) {
855 for (i
= m
->nitems
% m
->page_sz
+ 1; i
<= m
->page_sz
; i
++) {
856 mvaddch(i
, cl
- 1, ACS_VLINE
);
857 mvaddch(i
, cl
+ cf
->menu
->width
+ 4, ACS_VLINE
);
862 mvprintw(ln
- 1, cl
, " %s ", m
->title
);
866 mvaddch(ln
- 1, cl
+ m
->width
+ 3, ACS_UARROW
);
867 if (m
->page_sz
&& curpg
< m
->nitems
/ m
->page_sz
)
868 mvaddch(ln
+ nlines
, cl
+ m
->width
+ 3, ACS_DARROW
);
871 DBG(FRONTEND
, dbgprint("ui: menu: draw end."));
874 static void ui_menu_goto(struct cfdisk
*cf
, int where
)
876 struct cfdisk_menudesc
*d
;
879 /* stop and begin/end fr vertical menus */
880 if (cf
->menu
->vertical
881 && (where
< 0 || (size_t) where
> cf
->menu
->nitems
- 1))
884 /* continue from begin/end */
886 where
= cf
->menu
->nitems
- 1;
887 else if ((size_t) where
> cf
->menu
->nitems
- 1)
889 if ((size_t) where
== cf
->menu
->idx
)
895 cf
->menu
->idx
= where
;
897 if (!menuitem_on_page(cf
, old
)) {
902 d
= menu_get_menuitem(cf
, old
);
903 ui_draw_menuitem(cf
, d
, old
);
905 d
= menu_get_menuitem(cf
, where
);
906 ui_draw_menuitem(cf
, d
, where
);
909 static int ui_menu_move(struct cfdisk
*cf
, int key
)
914 if (cf
->menu
->vertical
)
918 case '\016': /* ^N */
919 case 'j': /* Vi-like alternative */
920 ui_menu_goto(cf
, cf
->menu
->idx
+ 1);
923 case '\020': /* ^P */
924 case 'k': /* Vi-like alternative */
925 ui_menu_goto(cf
, cf
->menu
->idx
- 1);
931 ui_menu_goto(cf
, cf
->menu
->nitems
);
938 ui_menu_goto(cf
, cf
->menu
->idx
+ 1);
944 ui_menu_goto(cf
, cf
->menu
->idx
- 1);
949 return 1; /* key irrelevant for menu move */
952 static void ui_draw_partition(struct cfdisk
*cf
, size_t i
)
954 int ln
= TABLE_START_LINE
+ 1 + i
; /* skip table header */
955 int cl
= ARROW_CURSOR_WIDTH
; /* we need extra space for cursor */
957 DBG(FRONTEND
, dbgprint("ui: draw partition %zu", i
));
959 if (cf
->lines_idx
== i
) {
961 mvaddstr(ln
, 0, ARROW_CURSOR_STRING
);
962 mvaddstr(ln
, cl
, cf
->lines
[i
+ 1]);
967 if (is_freespace(cf
, i
)) {
968 attron(COLOR_PAIR(CFDISK_CL_FREESPACE
));
971 mvaddstr(ln
, 0, ARROW_CURSOR_DUMMY
);
972 mvaddstr(ln
, cl
, cf
->lines
[i
+ 1]);
974 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE
));
979 static int ui_draw_table(struct cfdisk
*cf
)
981 int cl
= ARROW_CURSOR_WIDTH
;
982 size_t i
, nparts
= fdisk_table_get_nents(cf
->table
);
984 DBG(FRONTEND
, dbgprint("ui: draw table"));
986 if (cf
->nlines
- 2 < cf
->lines_idx
)
987 cf
->lines_idx
= cf
->nlines
- 2; /* don't count header */
991 mvaddstr(TABLE_START_LINE
, cl
, cf
->lines
[0]);
994 /* print partitions */
995 for (i
= 0; i
< nparts
; i
++)
996 ui_draw_partition(cf
, i
);
1001 static int ui_table_goto(struct cfdisk
*cf
, int where
)
1004 size_t nparts
= fdisk_table_get_nents(cf
->table
);
1006 DBG(FRONTEND
, dbgprint("ui: goto table %d", where
));
1010 else if ((size_t) where
> nparts
- 1)
1013 if ((size_t) where
== cf
->lines_idx
)
1016 old
= cf
->lines_idx
;
1017 cf
->lines_idx
= where
;
1019 ui_draw_partition(cf
, old
); /* cleanup old */
1020 ui_draw_partition(cf
, where
); /* draw new */
1027 static int ui_refresh(struct cfdisk
*cf
)
1030 uint64_t bytes
= cf
->cxt
->total_sectors
* cf
->cxt
->sector_size
;
1031 char *strsz
= size_to_human_string(SIZE_SUFFIX_SPACE
1032 | SIZE_SUFFIX_3LETTER
, bytes
);
1040 ui_center(0, _("Disk: %s"), cf
->cxt
->dev_path
);
1042 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
1043 strsz
, bytes
, (uintmax_t) cf
->cxt
->total_sectors
);
1044 if (fdisk_get_disklabel_id(cf
->cxt
, &id
) == 0 && id
)
1045 ui_center(2, _("Label: %s, identifier: %s"),
1046 cf
->cxt
->label
->name
, id
);
1048 ui_center(2, _("Label: %s"));
1057 static ssize_t
ui_get_string(struct cfdisk
*cf
, const char *prompt
,
1058 const char *hint
, char *buf
, size_t len
)
1061 ssize_t i
= 0, rc
= -1;
1063 int ln
= MENU_START_LINE
, cl
= 1;
1073 mvaddstr(ln
, cl
, prompt
);
1074 cl
+= mbs_safe_width(prompt
);
1080 cells
= mbs_safe_width(buf
);
1081 mvaddstr(ln
, cl
, buf
);
1089 move(ln
, cl
+ cells
);
1094 #if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1095 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1096 if (get_wch(&c
) == ERR
) {
1098 if ((c
= getch()) == ERR
) {
1100 if (!isatty(STDIN_FILENO
))
1105 if (c
== '\r' || c
== '\n' || c
== KEY_ENTER
)
1110 rc
= -CFDISK_ERR_ESC
;
1117 i
= mbs_truncate(buf
, &cells
);
1120 mvaddch(ln
, cl
+ cells
, ' ');
1121 move(ln
, cl
+ cells
);
1126 #if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1127 if (i
+ 1 < (ssize_t
) len
&& iswprint(c
)) {
1128 wchar_t wc
= (wchar_t) c
;
1129 char s
[MB_CUR_MAX
+ 1];
1130 int sz
= wctomb(s
, wc
);
1132 if (sz
> 0 && sz
+ i
< (ssize_t
) len
) {
1134 mvaddnstr(ln
, cl
+ cells
, s
, sz
);
1135 memcpy(buf
+ i
, s
, sz
);
1138 cells
+= wcwidth(wc
);
1143 if (i
+ 1 < (ssize_t
) len
&& isprint(c
)) {
1144 mvaddch(ln
, cl
+ cells
, c
);
1156 rc
= i
; /* success */
1166 /* @res is default value as well as result in bytes */
1167 static int ui_get_size(struct cfdisk
*cf
, const char *prompt
, uintmax_t *res
,
1168 uintmax_t low
, uintmax_t up
)
1173 char *dflt
= size_to_human_string(0, *res
);
1175 DBG(FRONTEND
, dbgprint("ui: get_size (default=%ju)", *res
));
1180 int pwr
= 0, insec
= 0;
1182 snprintf(buf
, sizeof(buf
), "%s", dflt
);
1183 rc
= ui_get_string(cf
, prompt
,
1184 _("May be followed by {M,B,G,T}iB "
1185 "(the \"iB\" is optional) or S for sectors."),
1188 ui_warnx(_("Please, specify size."));
1189 continue; /* nothing specified */
1190 } else if (rc
== -CFDISK_ERR_ESC
)
1191 break; /* cancel dialog */
1193 if (strcmp(buf
, dflt
) == 0)
1194 user
= *res
, rc
= 0; /* no change, use default */
1196 size_t len
= strlen(buf
);
1197 if (buf
[len
- 1] == 'S') {
1199 buf
[len
- 1] = '\0';
1201 rc
= parse_size(buf
, &user
, &pwr
); /* parse */
1205 DBG(FRONTEND
, dbgprint("ui: get_size user=%ju, power=%d, sectors=%s",
1206 user
, pwr
, insec
? "yes" : "no"));
1208 user
*= cf
->cxt
->sector_size
;
1210 ui_warnx(_("Minimal size is %ju"), low
);
1213 if (user
> up
&& pwr
&& user
< up
+ (1ULL << pwr
* 10))
1214 /* ignore when the user specified size overflow
1215 * with in range specified by suffix (e.g. MiB) */
1219 ui_warnx(_("Maximal size is %ju bytes."), up
);
1223 ui_warnx(_("Failed to parse size."));
1230 DBG(FRONTEND
, dbgprint("ui: get_size (result=%ju, rc=%zd)", *res
, rc
));
1234 static struct fdisk_parttype
*ui_get_parttype(struct cfdisk
*cf
,
1235 struct fdisk_parttype
*cur
)
1237 struct cfdisk_menudesc
*d
, *cm
;
1238 size_t i
= 0, nitems
, idx
= 0;
1239 struct fdisk_parttype
*t
= NULL
;
1240 int has_typestr
= 0;
1242 DBG(FRONTEND
, dbgprint("ui: asking for parttype."));
1244 /* create cfdisk menu according to label types, note that the
1245 * last cm[] item has to be empty -- so nitems + 1 */
1246 nitems
= cf
->cxt
->label
->nparttypes
;
1249 cm
= xcalloc(nitems
+ 1, sizeof(struct cfdisk_menudesc
));
1253 has_typestr
= cf
->cxt
->label
->parttypes
[0].typestr
&&
1254 *cf
->cxt
->label
->parttypes
[0].typestr
;
1256 for (i
= 0; i
< nitems
; i
++) {
1257 struct fdisk_parttype
*x
= &cf
->cxt
->label
->parttypes
[i
];
1264 xasprintf(&name
, "%2x %s", x
->type
, x
->name
);
1266 name
= (char *) x
->name
;
1267 cm
[i
].desc
= x
->typestr
;
1274 /* make the new menu active */
1276 cf
->menu
->vertical
= 1;
1277 cf
->menu
->idx
= idx
;
1278 menu_set_title(cf
->menu
, _("Select partition type"));
1284 if (ui_menu_move(cf
, key
) == 0)
1290 d
= menu_get_menuitem(cf
, cf
->menu
->idx
);
1292 t
= (struct fdisk_parttype
*) d
->userdata
;
1303 for (i
= 0; i
< nitems
; i
++)
1304 free((char *) cm
[i
].name
);
1307 DBG(FRONTEND
, dbgprint("ui: get parrtype done [type=%s] ", t
? t
->name
: NULL
));
1311 /* prints menu with libfdisk labels and waits for users response */
1312 static int ui_create_label(struct cfdisk
*cf
)
1314 struct cfdisk_menudesc
*d
, *cm
;
1316 size_t i
= 0, nitems
;
1317 struct fdisk_label
*lb
= NULL
;
1321 DBG(FRONTEND
, dbgprint("ui: asking for new disklabe."));
1323 /* create cfdisk menu according to libfdisk labels, note that the
1324 * last cm[] item has to be empty -- so nitems + 1 */
1325 nitems
= fdisk_context_get_nlabels(cf
->cxt
);
1326 cm
= xcalloc(nitems
+ 1, sizeof(struct cfdisk_menudesc
));
1328 for (i
= 0; i
< nitems
; i
++) {
1329 if (fdisk_context_next_label(cf
->cxt
, &lb
))
1331 cm
[i
].name
= lb
->name
;
1335 ui_center(LINES
- 4,
1336 _("Device does not contain a recognized partition table."));
1337 ui_center(LINES
- 3,
1338 _("Please, select a type to create a new disk label."));
1340 /* make the new menu active */
1342 cf
->menu
->vertical
= 1;
1343 menu_set_title(cf
->menu
, _("Select label type"));
1349 if (ui_menu_move(cf
, key
) == 0)
1355 d
= menu_get_menuitem(cf
, cf
->menu
->idx
);
1357 rc
= fdisk_create_disklabel(cf
->cxt
, d
->name
);
1368 DBG(FRONTEND
, dbgprint("ui: create label done [rc=%d] ", rc
));
1372 static int ui_help(void)
1376 static const char *help
[] = {
1377 N_("Help Screen for cfdisk"),
1379 N_("This is cfdisk, a curses based disk partitioning program, which"),
1380 N_("allows you to create, delete and modify partitions on your hard"),
1383 N_("Copyright (C) 2014 Karel Zak <kzak@redhat.com> "),
1384 N_("Based on the original cfdisk from Kevin E. Martin & aeb."),
1386 N_("Command Meaning"),
1387 N_("------- -------"),
1388 N_(" b Toggle bootable flag of the current partition"),
1389 N_(" d Delete the current partition"),
1390 N_(" h Print this screen"),
1391 N_(" n Create new partition from free space"),
1392 N_(" q Quit program without writing partition table"),
1393 N_(" t Change the partition type"),
1394 N_(" W Write partition table to disk (must enter upper case W)"),
1395 N_(" Since this might destroy data on the disk, you must"),
1396 N_(" either confirm or deny the write by entering `yes' or"),
1398 N_("Up Arrow Move cursor to the previous partition"),
1399 N_("Down Arrow Move cursor to the next partition"),
1400 N_("Left Arrow Move cursor to the previous menu item"),
1401 N_("Right Arrow Move cursor to the next menu item"),
1404 N_("Note: All of the commands can be entered with either upper or lower"),
1405 N_("case letters (except for Writes)."),
1407 N_("Use lsblk(8) or partx(8) to see more details about the device.")
1412 for (i
= 0; i
< ARRAY_SIZE(help
); i
++)
1413 mvaddstr(i
, 1, _(help
[i
]));
1415 ui_info(_("Press a key to continue."));
1420 /* TODO: use @sz, now 128bytes */
1421 static int main_menu_ignore_keys(struct cfdisk
*cf
, char *ignore
,
1422 size_t sz
__attribute__((__unused__
)))
1424 struct fdisk_partition
*pa
= get_current_partition(cf
);
1429 if (fdisk_partition_is_freespace(pa
)) {
1430 ignore
[i
++] = 'd'; /* delete */
1431 ignore
[i
++] = 't'; /* set type */
1432 ignore
[i
++] = 'b'; /* set bootable */
1435 if (!fdisk_is_disklabel(cf
->cxt
, DOS
) &&
1436 !fdisk_is_disklabel(cf
->cxt
, SGI
))
1443 /* returns: error: < 0, success: 0, quit: 1 */
1444 static int main_menu_action(struct cfdisk
*cf
, int key
)
1448 const char *info
= NULL
, *warn
= NULL
;
1449 struct fdisk_partition
*pa
;
1456 struct cfdisk_menudesc
*d
= menu_get_menuitem(cf
, cf
->menu
->idx
);
1461 } else if (key
!= 'w' && key
!= 'W')
1462 key
= tolower(key
); /* case insensitive except 'W'rite */
1464 DBG(FRONTEND
, dbgprint("ui: main menu action: key=%c", key
));
1466 if (cf
->menu
->ignore
&& strchr(cf
->menu
->ignore
, key
)) {
1467 DBG(FRONTEND
, dbgprint(" ignore '%c'", key
));
1471 pa
= get_current_partition(cf
);
1472 n
= fdisk_partition_get_partno(pa
);
1474 DBG(FRONTEND
, dbgprint("menu action on %p", pa
));
1477 case 'b': /* Bootable flag */
1479 int fl
= fdisk_is_disklabel(cf
->cxt
, DOS
) ? DOS_FLAG_ACTIVE
:
1480 fdisk_is_disklabel(cf
->cxt
, SGI
) ? SGI_FLAG_BOOT
: 0;
1482 if (fl
&& fdisk_partition_toggle_flag(cf
->cxt
, n
, fl
))
1483 warn
= _("Could not toggle the flag.");
1488 case 'd': /* Delete */
1489 if (fdisk_delete_partition(cf
->cxt
, n
) != 0)
1490 warn
= _("Could not delete partition %zu.");
1492 info
= _("Partition %zu has been deleted.");
1495 case 'h': /* help */
1501 uint64_t start
, size
, dflt_size
;
1502 struct fdisk_partition
*npa
; /* the new partition */
1504 if (!pa
|| !fdisk_partition_is_freespace(pa
))
1506 npa
= fdisk_new_partition();
1509 /* free space range */
1510 start
= fdisk_partition_get_start(pa
);
1511 size
= dflt_size
= fdisk_partition_get_size(pa
) * cf
->cxt
->sector_size
;
1513 if (ui_get_size(cf
, _("Partition size: "), &size
, 1, size
)
1517 if (dflt_size
== size
) /* default is to fillin all free space */
1518 fdisk_partition_end_follow_default(npa
, 1);
1519 else /* set relative size of the partition */
1520 fdisk_partition_set_size(npa
, size
/ cf
->cxt
->sector_size
);
1522 fdisk_partition_set_start(npa
, start
);
1523 fdisk_partition_partno_follow_default(npa
, 1);
1524 /* add to disk label -- libfdisk will ask for missing details */
1525 rc
= fdisk_add_partition(cf
->cxt
, npa
);
1526 fdisk_unref_partition(npa
);
1531 case 'q': /* Quit */
1533 case 't': /* Type */
1536 struct fdisk_parttype
*t
;
1538 if (!pa
|| fdisk_partition_is_freespace(pa
))
1540 t
= (struct fdisk_parttype
*) fdisk_partition_get_type(pa
);
1541 old
= t
? t
->name
: _("Unknown");
1543 t
= ui_get_parttype(cf
, t
);
1546 if (t
&& fdisk_set_partition_type(cf
->cxt
, n
, t
) == 0)
1547 ui_info(_("Changed type of partition '%s' to '%s'."),
1548 old
, t
? t
->name
: _("Unknown"));
1550 ui_info(_("Type of partition %zu is unchanged: %s."),
1554 case 'W': /* Write */
1555 rc
= fdisk_write_disklabel(cf
->cxt
);
1557 ui_errx(EXIT_FAILURE
, _("failed to write disklabel"));
1558 fdisk_reread_partition_table(cf
->cxt
);
1559 ui_info(_("The partition table has been altered."));
1575 static int ui_run(struct cfdisk
*cf
)
1579 DBG(FRONTEND
, dbgprint("ui: start COLS=%d, LINES=%d", COLS
, LINES
));
1581 if (!fdisk_dev_has_disklabel(cf
->cxt
)) {
1582 rc
= ui_create_label(cf
);
1584 ui_errx(EXIT_FAILURE
,
1585 _("failed to create a new disklabel"));
1591 rc
= lines_refresh(cf
);
1593 ui_errx(EXIT_FAILURE
, _("failed to read partitions"));
1595 menu_push(cf
, main_menudesc
);
1596 cf
->menu
->ignore_cb
= main_menu_ignore_keys
;
1598 rc
= ui_refresh(cf
);
1603 int rc
= 0, key
= getch();
1605 if (ui_menu_move(cf
, key
) == 0)
1610 case '\016': /* ^N */
1611 case 'j': /* Vi-like alternative */
1612 ui_table_goto(cf
, cf
->lines_idx
+ 1);
1615 case '\020': /* ^P */
1616 case 'k': /* Vi-like alternative */
1617 ui_table_goto(cf
, cf
->lines_idx
- 1);
1620 ui_table_goto(cf
, 0);
1623 ui_table_goto(cf
, cf
->nlines
- 1);
1628 rc
= main_menu_action(cf
, 0);
1631 rc
= main_menu_action(cf
, key
);
1643 DBG(FRONTEND
, dbgprint("ui: end"));
1648 int main(int argc
, char *argv
[])
1651 struct cfdisk _cf
= { .lines_idx
= 0 },
1654 setlocale(LC_ALL
, "");
1655 bindtextdomain(PACKAGE
, LOCALEDIR
);
1656 textdomain(PACKAGE
);
1657 atexit(close_stdout
);
1659 fdisk_init_debug(0);
1660 cf
->cxt
= fdisk_new_context();
1662 err(EXIT_FAILURE
, _("failed to allocate libfdisk context"));
1664 fdisk_context_set_ask(cf
->cxt
, ask_callback
, (void *) cf
);
1667 err(EXIT_FAILURE
, "usage: %s <device>", argv
[0]);
1669 if (fdisk_context_assign_device(cf
->cxt
, argv
[optind
], 0) != 0)
1670 err(EXIT_FAILURE
, _("cannot open %s"), argv
[optind
]);
1672 /* Don't use err(), warn() from this point */
1679 fdisk_unref_table(cf
->table
);
1681 rc
= fdisk_context_deassign_device(cf
->cxt
);
1682 fdisk_free_context(cf
->cxt
);
1683 DBG(FRONTEND
, dbgprint("bye! [rc=%d]", rc
));
1684 return rc
== 0 ? EXIT_SUCCESS
: EXIT_FAILURE
;