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)
47 typedef int (menu_callback_t
)(struct cfdisk
*, int);
49 static int menu_cb_main(struct cfdisk
*cf
, int key
);
52 struct cfdisk_menudesc
{
53 int key
; /* keyboard shortcut */
54 const char *name
; /* item name */
55 const char *desc
; /* item description */
59 struct cfdisk_menudesc
*desc
;
65 menu_callback_t
*callback
;
67 struct cfdisk_menu
*prev
;
70 static struct cfdisk_menudesc menu_main
[] = {
71 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
72 { 'd', N_("Delete"), N_("Delete the current partition") },
73 // { 'g', N_("Geometry"), N_("Change disk geometry (experts only)") },
74 // { 'h', N_("Help"), N_("Print help screen") },
75 // { 'm', N_("Maximize"), N_("Maximize disk usage of the current partition (experts only)") },
76 { 'n', N_("New"), N_("Create new partition from free space") },
77 // { 'p', N_("Print"), N_("Print partition table to the screen or to a file") },
78 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
79 { 't', N_("Type"), N_("Change the partition type") },
80 // { 'u', N_("Units"), N_("Change units of the partition size display (MB, sect, cyl)") },
81 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
89 static struct cfdisk_menudesc
*menus
[] = {
90 [CFDISK_MENU_MAIN
] = menu_main
93 static menu_callback_t
*menu_callbacks
[] = {
94 [CFDISK_MENU_MAIN
] = menu_cb_main
99 struct fdisk_context
*cxt
; /* libfdisk context */
100 struct fdisk_table
*table
; /* partition table */
102 struct cfdisk_menu
*menu
; /* the current menu */
105 int *cols
; /* output columns */
106 size_t ncols
; /* number of columns */
108 char *linesbuf
; /* table as string */
109 size_t linesbufsz
; /* size of the tb_buf */
111 char **lines
; /* array with lines */
112 size_t nlines
; /* number of lines */
113 size_t lines_idx
; /* current line <0..N>, exclude header */
115 unsigned int ui_enabled
: 1;
118 static int cols_init(struct cfdisk
*cf
)
126 return fdisk_get_columns(cf
->cxt
, 0, &cf
->cols
, &cf
->ncols
);
129 /* It would be possible to use fdisk_table_to_string(), but we want some
130 * extension to the output format, so let's do it without libfdisk
132 static char *table_to_string(struct cfdisk
*cf
, struct fdisk_table
*tb
)
134 struct fdisk_partition
*pa
;
135 const struct fdisk_column
*col
;
136 struct fdisk_label
*lb
;
137 struct fdisk_iter
*itr
= NULL
;
138 struct tt
*tt
= NULL
;
142 DBG(FRONTEND
, dbgprint("table: convert to string"));
149 lb
= fdisk_context_get_label(cf
->cxt
, NULL
);
152 tt
= tt_new_table(TT_FL_FREEDATA
| TT_FL_MAX
);
155 itr
= fdisk_new_iter(FDISK_ITER_FORWARD
);
160 for (i
= 0; i
< cf
->ncols
; i
++) {
161 col
= fdisk_label_get_column(lb
, cf
->cols
[i
]);
163 tt_define_column(tt
, col
->name
,
169 while (fdisk_table_next_partition(tb
, itr
, &pa
) == 0) {
170 struct tt_line
*ln
= tt_add_line(tt
, NULL
);
173 for (i
= 0; i
< cf
->ncols
; i
++) {
176 col
= fdisk_label_get_column(lb
, cf
->cols
[i
]);
179 if (fdisk_partition_to_string(pa
, cf
->cxt
, col
->id
, &cdata
))
181 tt_line_set_data(ln
, i
, cdata
);
185 if (!tt_is_empty(tt
)) {
186 tt_set_termreduce(tt
, ARROW_CURSOR_WIDTH
);
187 tt_print_table_to_string(tt
, &res
);
191 fdisk_free_iter(itr
);
196 static int lines_refresh(struct cfdisk
*cf
)
204 DBG(FRONTEND
, dbgprint("refresing buffer"));
213 fdisk_unref_table(cf
->table
);
214 fdisk_context_enable_freespace(cf
->cxt
, 1);
216 rc
= fdisk_get_table(cf
->cxt
, &cf
->table
);
220 cf
->linesbuf
= table_to_string(cf
, cf
->table
);
224 cf
->linesbufsz
= strlen(cf
->linesbuf
);
225 cf
->nlines
= fdisk_table_get_nents(cf
->table
) + 1; /* 1 for header line */
227 cf
->lines
= calloc(cf
->nlines
, sizeof(char *));
231 for (p
= cf
->linesbuf
, i
= 0; p
&& i
< cf
->nlines
; i
++) {
243 static struct fdisk_partition
*get_current_partition(struct cfdisk
*cf
)
248 return fdisk_table_get_partition(cf
->table
, cf
->lines_idx
);
251 static int ask_callback(struct fdisk_context
*cxt
, struct fdisk_ask
*ask
,
252 void *data
__attribute__((__unused__
)))
259 switch(fdisk_ask_get_type(ask
)) {
260 case FDISK_ASKTYPE_INFO
:
261 fputs(fdisk_ask_print_get_mesg(ask
), stdout
);
264 case FDISK_ASKTYPE_WARNX
:
265 fputs(fdisk_ask_print_get_mesg(ask
), stderr
);
268 case FDISK_ASKTYPE_WARN
:
269 fputs(fdisk_ask_print_get_mesg(ask
), stderr
);
270 errno
= fdisk_ask_print_get_errno(ask
);
271 fprintf(stderr
, ": %m\n");
274 warnx(_("internal error: unsupported dialog type %d"), fdisk_ask_get_type(ask
));
281 static int ui_end(struct cfdisk
*cf
)
283 if (cf
&& !cf
->ui_enabled
)
286 #if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
287 SLsmg_gotorc(LINES
- 1, 0);
290 mvcur(0, COLS
- 1, LINES
-1, 0);
298 static void ui_print_center(int line
, const char *fmt
, ...)
308 xvasprintf(&buf
, fmt
, ap
);
311 width
= strlen(buf
); /* TODO: count cells! */
312 mvaddstr(line
, (COLS
- width
) / 2, buf
);
317 static void die_on_signal(int dummy
__attribute__((__unused__
)))
323 static void menu_update_ignore(struct cfdisk
*cf
)
325 char ignore
[128] = { 0 };
327 struct fdisk_partition
*pa
;
328 struct cfdisk_menu
*m
;
329 struct cfdisk_menudesc
*d
;
334 DBG(FRONTEND
, dbgprint("menu: update menu ignored keys"));
337 case CFDISK_MENU_MAIN
:
338 pa
= get_current_partition(cf
);
341 if (fdisk_partition_is_freespace(pa
)) {
342 ignore
[i
++] = 'd'; /* delete */
343 ignore
[i
++] = 't'; /* set type */
344 ignore
[i
++] = 'b'; /* set bootable */
353 /* return if no change */
354 if ( (!m
->ignore
&& !*ignore
)
355 || (m
->ignore
&& *ignore
&& strcmp(m
->ignore
, ignore
) == 0)) {
360 m
->ignore
= xstrdup(ignore
);
363 for (d
= m
->desc
; d
->name
; d
++) {
364 if (m
->ignore
&& strchr(m
->ignore
, d
->key
))
370 static struct cfdisk_menu
*menu_push(struct cfdisk
*cf
, size_t id
)
372 struct cfdisk_menu
*m
= xcalloc(1, sizeof(*m
));
373 struct cfdisk_menudesc
*d
;
376 assert(id
< ARRAY_SIZE(menus
));
378 DBG(FRONTEND
, dbgprint("menu: new menu"));
383 m
->callback
= menu_callbacks
[id
];
385 for (d
= m
->desc
; d
->name
; d
++) {
386 const char *name
= _(d
->name
);
387 size_t len
= strlen(name
); /* TODO: we care about cells! */
397 static struct cfdisk_menu
*menu_pop(struct cfdisk
*cf
)
399 struct cfdisk_menu
*m
= NULL
;
403 DBG(FRONTEND
, dbgprint("menu: rem menu"));
407 free(cf
->menu
->ignore
);
414 /* returns: error: < 0, success: 0, quit: 1 */
415 static int menu_cb_main(struct cfdisk
*cf
, int key
)
418 case 'd': /* Delete */
426 case 'W': /* Write */
434 static int ui_init(struct cfdisk
*cf
)
438 DBG(FRONTEND
, dbgprint("ui: init"));
440 /* setup SIGCHLD handler */
441 sigemptyset(&sa
.sa_mask
);
443 sa
.sa_handler
= die_on_signal
;
444 sigaction(SIGINT
, &sa
, NULL
);
445 sigaction(SIGTERM
, &sa
, NULL
);
454 keypad(stdscr
, TRUE
);
459 static size_t menuitem_get_line(struct cfdisk
*cf
, size_t idx
)
461 size_t len
= cf
->menu
->width
+ 4 + MENU_PADDING
; /* item width */
462 size_t items
= COLS
/ len
; /* items per line */
464 return MENU_START_LINE
+ ((idx
/ items
));
467 static int menuitem_get_column(struct cfdisk
*cf
, size_t idx
)
469 size_t len
= cf
->menu
->width
+ 4 + MENU_PADDING
; /* item width */
470 size_t items
= COLS
/ len
; /* items per line */
471 size_t extra
= items
< cf
->menu
->nitems
? /* extra space on line */
472 COLS
% len
: /* - multi-line menu */
473 COLS
- (cf
->menu
->nitems
* len
); /* - one line menu */
475 extra
+= MENU_PADDING
; /* add padding after last item to extra */
478 return (idx
* len
) + (extra
/ 2);
479 return ((idx
% items
) * len
) + (extra
/ 2);
482 static struct cfdisk_menudesc
*menu_get_menuitem(struct cfdisk
*cf
, size_t idx
)
484 struct cfdisk_menudesc
*d
;
487 for (i
= 0, d
= cf
->menu
->desc
; d
->name
; d
++) {
488 if (cf
->menu
->ignore
&& strchr(cf
->menu
->ignore
, d
->key
))
497 static void ui_draw_menuitem(struct cfdisk
*cf
,
498 struct cfdisk_menudesc
*d
,
501 char buf
[80 * MB_CUR_MAX
];
503 size_t width
= cf
->menu
->width
+ 2; /* 2 = blank around string */
507 mbsalign(name
, buf
, sizeof(buf
), &width
, MBS_ALIGN_CENTER
, 0);
509 ln
= menuitem_get_line(cf
, idx
);
510 cl
= menuitem_get_column(cf
, idx
);
512 DBG(FRONTEND
, dbgprint("ui: menuitem: cl=%d, ln=%d, item='%s'",
515 if (cf
->menu_idx
== idx
) {
517 mvprintw(ln
, cl
, "[%s]", buf
);
520 ui_print_center(LINES
- 1, d
->desc
);
522 mvprintw(ln
, cl
, "[%s]", buf
);
525 static void ui_draw_menu(struct cfdisk
*cf
)
527 struct cfdisk_menudesc
*d
;
533 DBG(FRONTEND
, dbgprint("ui: menu: draw start"));
535 for (i
= MENU_START_LINE
; i
< (size_t) LINES
- 1; i
++) {
540 menu_update_ignore(cf
);
543 while ((d
= menu_get_menuitem(cf
, i
)))
544 ui_draw_menuitem(cf
, d
, i
++);
546 DBG(FRONTEND
, dbgprint("ui: menu: draw end."));
549 static void ui_menu_goto(struct cfdisk
*cf
, int where
)
551 struct cfdisk_menudesc
*d
;
555 where
= cf
->menu
->nitems
- 1;
556 else if ((size_t) where
> cf
->menu
->nitems
- 1)
558 if ((size_t) where
== cf
->menu_idx
)
562 cf
->menu_idx
= where
;
564 d
= menu_get_menuitem(cf
, old
);
565 ui_draw_menuitem(cf
, d
, old
);
567 d
= menu_get_menuitem(cf
, where
);
568 ui_draw_menuitem(cf
, d
, where
);
571 /* returns: error: < 0, success: 0, quit: 1 */
572 static int ui_menu_action(struct cfdisk
*cf
, int key
)
576 assert(cf
->menu
->callback
);
579 struct cfdisk_menudesc
*d
= menu_get_menuitem(cf
, cf
->menu_idx
);
584 } else if (key
!= 'w' && key
!= 'W')
585 key
= tolower(key
); /* case insensitive except 'W'rite */
587 DBG(FRONTEND
, dbgprint("ui: menu action: key=%c", key
));
589 if (cf
->menu
->ignore
&& strchr(cf
->menu
->ignore
, key
)) {
590 DBG(FRONTEND
, dbgprint(" ignore '%c'", key
));
594 return cf
->menu
->callback(cf
, key
);
597 static void ui_draw_partition(struct cfdisk
*cf
, size_t i
)
599 int ln
= TABLE_START_LINE
+ 1 + i
; /* skip table header */
600 int cl
= ARROW_CURSOR_WIDTH
; /* we need extra space for cursor */
602 DBG(FRONTEND
, dbgprint("ui: draw partition %zu", i
));
604 if (cf
->lines_idx
== i
) {
606 mvaddstr(ln
, 0, ARROW_CURSOR_STRING
);
607 mvaddstr(ln
, cl
, cf
->lines
[i
+ 1]);
610 mvaddstr(ln
, 0, ARROW_CURSOR_DUMMY
);
611 mvaddstr(ln
, cl
, cf
->lines
[i
+ 1]);
616 static int ui_draw_table(struct cfdisk
*cf
)
618 int cl
= ARROW_CURSOR_WIDTH
;
619 size_t i
, nparts
= fdisk_table_get_nents(cf
->table
);
621 DBG(FRONTEND
, dbgprint("ui: draw table"));
623 if (cf
->nlines
- 2 < cf
->lines_idx
)
624 cf
->lines_idx
= cf
->nlines
- 2; /* don't count header */
628 mvaddstr(TABLE_START_LINE
, cl
, cf
->lines
[0]);
631 /* print partitions */
632 for (i
= 0; i
< nparts
; i
++)
633 ui_draw_partition(cf
, i
);
638 static int ui_table_goto(struct cfdisk
*cf
, int where
)
641 size_t nparts
= fdisk_table_get_nents(cf
->table
);
643 DBG(FRONTEND
, dbgprint("ui: goto table %d", where
));
647 else if ((size_t) where
> nparts
- 1)
650 if ((size_t) where
== cf
->lines_idx
)
654 cf
->lines_idx
= where
;
656 ui_draw_partition(cf
, old
); /* cleanup old */
657 ui_draw_partition(cf
, where
); /* draw new */
663 static int ui_refresh(struct cfdisk
*cf
)
666 uint64_t bytes
= cf
->cxt
->total_sectors
* cf
->cxt
->sector_size
;
667 char *strsz
= size_to_human_string(SIZE_SUFFIX_SPACE
668 | SIZE_SUFFIX_3LETTER
, bytes
);
676 ui_print_center(0, _("Disk: %s"), cf
->cxt
->dev_path
);
678 ui_print_center(1, _("Size: %s, %ju bytes, %ju sectors"),
679 strsz
, bytes
, (uintmax_t) cf
->cxt
->total_sectors
);
680 if (fdisk_get_disklabel_id(cf
->cxt
, &id
) == 0 && id
)
681 ui_print_center(2, _("Label: %s, identifier: %s"),
682 cf
->cxt
->label
->name
, id
);
684 ui_print_center(2, _("Label: %s"));
693 static int ui_run(struct cfdisk
*cf
)
697 DBG(FRONTEND
, dbgprint("ui: start COLS=%d, LINES=%d", COLS
, LINES
));
699 menu_push(cf
, CFDISK_MENU_MAIN
);
706 int rc
= 0, key
= getch();
710 case '\016': /* ^N */
711 case 'j': /* Vi-like alternative */
712 ui_table_goto(cf
, cf
->lines_idx
+ 1);
715 case '\020': /* ^P */
716 case 'k': /* Vi-like alternative */
717 ui_table_goto(cf
, cf
->lines_idx
- 1);
720 ui_table_goto(cf
, 0);
723 ui_table_goto(cf
, cf
->nlines
- 1);
725 ui_menu_action(cf
, 0);
731 ui_menu_goto(cf
, cf
->menu_idx
- 1);
735 ui_menu_goto(cf
, cf
->menu_idx
+ 1);
740 rc
= ui_menu_action(cf
, 0);
743 rc
= ui_menu_action(cf
, key
);
755 DBG(FRONTEND
, dbgprint("ui: end"));
760 int main(int argc
, char *argv
[])
762 struct cfdisk _cf
= { .lines_idx
= 0 },
765 setlocale(LC_ALL
, "");
766 bindtextdomain(PACKAGE
, LOCALEDIR
);
768 atexit(close_stdout
);
771 cf
->cxt
= fdisk_new_context();
773 err(EXIT_FAILURE
, _("failed to allocate libfdisk context"));
775 fdisk_context_set_ask(cf
->cxt
, ask_callback
, (void *) cf
);
776 fdisk_context_enable_freespace(cf
->cxt
, 1);
779 err(EXIT_FAILURE
, "usage: %s <device>", argv
[0]);
781 if (fdisk_context_assign_device(cf
->cxt
, argv
[optind
], 0) != 0)
782 err(EXIT_FAILURE
, _("cannot open %s"), argv
[optind
]);
786 if (lines_refresh(cf
))
787 errx(EXIT_FAILURE
, _("failed to read partitions"));
789 /* Don't use err(), warn() from this point */
796 fdisk_unref_table(cf
->table
);
797 fdisk_free_context(cf
->cxt
);