]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/cfdisk.c
cfdisk: add --lock and LOCK_BLOCK_DEVICE
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
CommitLineData
2ddfd9e4
BS
1/*
2 * cfdisk.c - Display or manipulate a disk partition table.
3 *
dda7fe12 4 * Copyright (C) 2014-2015 Karel Zak <kzak@redhat.com>
2ddfd9e4
BS
5 * Copyright (C) 1994 Kevin E. Martin (martin@cs.unc.edu)
6 *
7 * The original cfdisk was inspired by the fdisk program
8 * by A. V. Le Blanc (leblanc@mcc.ac.uk.
9 *
10 * cfdisk is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
14 */
6dbe3af9
KZ
15#include <stdlib.h>
16#include <stdio.h>
6dbe3af9 17#include <errno.h>
8c3a5a44 18#include <signal.h>
00b4f26a 19#include <ctype.h>
04915c3f 20#include <getopt.h>
57c83d63 21#include <assert.h>
baa3b270 22#include <libsmartcols.h>
7556c944 23#include <sys/ioctl.h>
57c83d63 24#include <libfdisk.h>
541e6934 25
dda7fe12
OO
26#ifdef HAVE_LIBMOUNT
27# include <libmount.h> /* keep it optional for non-linux systems */
28#endif
29
541e6934 30#ifdef HAVE_SLANG_H
3b411726 31# include <slang.h>
541e6934 32#elif defined(HAVE_SLANG_SLANG_H)
3b411726 33# include <slang/slang.h>
541e6934
KZ
34#endif
35
cc6ab5ef
PS
36#ifndef _XOPEN_SOURCE
37# define _XOPEN_SOURCE 500 /* for inclusion of get_wch */
38#endif
39
48d7b13a 40#ifdef HAVE_SLCURSES_H
3b411726 41# include <slcurses.h>
48d7b13a 42#elif defined(HAVE_SLANG_SLCURSES_H)
3b411726 43# include <slang/slcurses.h>
30c97bb8 44#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
3b411726 45# include <ncursesw/ncurses.h>
48d7b13a 46#elif defined(HAVE_NCURSES_H)
3b411726 47# include <ncurses.h>
48d7b13a 48#elif defined(HAVE_NCURSES_NCURSES_H)
3b411726 49# include <ncurses/ncurses.h>
2b6fc908 50#endif
541e6934 51
5f94ca33 52#ifdef HAVE_WIDECHAR
3b411726 53# include <wctype.h>
f1512be8 54# include <wchar.h>
5f94ca33
KZ
55#endif
56
8c3a5a44 57#include "c.h"
b2d28533 58#include "closestream.h"
7eda085c 59#include "nls.h"
8abcf290 60#include "strutils.h"
8c3a5a44 61#include "xalloc.h"
5f94ca33 62#include "mbsalign.h"
1b6d02fe 63#include "mbsedit.h"
04915c3f 64#include "colors.h"
57c83d63 65#include "debug.h"
dda7fe12 66#include "list.h"
ec8f7121 67#include "blkdev.h"
8c3a5a44 68
6e51ab0c 69static const char *default_disks[] = {
a4f0a42d 70#ifdef __GNU__
6e51ab0c
KZ
71 "/dev/hd0",
72 "/dev/sd0",
a4f0a42d 73#elif defined(__FreeBSD__)
6e51ab0c
KZ
74 "/dev/ad0",
75 "/dev/da0",
a4f0a42d 76#else
6e51ab0c
KZ
77 "/dev/sda",
78 "/dev/vda",
79 "/dev/hda",
a4f0a42d 80#endif
6e51ab0c 81};
a4f0a42d 82
c743bf74 83#define ARROW_CURSOR_STRING ">> "
8c3a5a44
KZ
84#define ARROW_CURSOR_DUMMY " "
85#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
86
5c04b408
KZ
87/* vertical menu */
88#define MENU_V_SPADDING 1 /* space around menu item string */
89
90/* horizontal menu */
91#define MENU_H_SPADDING 0 /* space around menu item string */
92#define MENU_H_BETWEEN 2 /* space between menu items */
93#define MENU_H_PRESTR "["
94#define MENU_H_POSTSTR "]"
95
96#define MENU_TITLE_PADDING 3
97
98#define MENU_H_PRESTR_SZ (sizeof(MENU_H_PRESTR) - 1)
99#define MENU_H_POSTSTR_SZ (sizeof(MENU_H_POSTSTR) - 1)
100
8c3a5a44 101#define TABLE_START_LINE 4
6e07b904 102#define MENU_START_LINE (ui_lines - 4) /* The menu maybe use two lines */
eef63970 103#define INFO_LINE (ui_lines - 2)
702d0e1c 104#define WARN_LINE INFO_LINE
eef63970 105#define HINT_LINE (ui_lines - 1)
b1f58330
KZ
106
107#define CFDISK_ERR_ESC 5000
108
109#ifndef KEY_ESC
110# define KEY_ESC '\033'
111#endif
112#ifndef KEY_DELETE
113# define KEY_DELETE '\177'
114#endif
1b6d02fe
KZ
115#ifndef KEY_DC
116# define KEY_DC 0423
117#endif
118
1af8003b
KZ
119
120/* colors */
121enum {
122 CFDISK_CL_NONE = 0,
45333e9d
KZ
123 CFDISK_CL_WARNING,
124 CFDISK_CL_FREESPACE,
f9fb8290 125 CFDISK_CL_INFO
1af8003b
KZ
126};
127static const int color_pairs[][2] = {
128 /* color foreground, background */
45333e9d 129 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
f9fb8290
KZ
130 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 },
131 [CFDISK_CL_INFO] = { COLOR_BLUE, -1 }
1af8003b 132};
8c3a5a44 133
8460875d 134struct cfdisk;
8460875d 135
3b411726
KZ
136static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
137static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
138static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
b1f58330 139static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
7556c944 140static void menu_refresh_size(struct cfdisk *cf);
83fa0f80 141
502c5186 142static int ui_end(void);
1af8003b 143static int ui_refresh(struct cfdisk *cf);
83fa0f80
KZ
144static void ui_warnx(const char *fmt, ...);
145static void ui_warn(const char *fmt, ...);
146static void ui_info(const char *fmt, ...);
b1f58330 147static void ui_draw_menu(struct cfdisk *cf);
ac27ea5c 148static int ui_menu_move(struct cfdisk *cf, int key);
7556c944 149static void ui_menu_resize(struct cfdisk *cf);
ac27ea5c 150
7085f1e4
KZ
151static int ui_get_size(struct cfdisk *cf, const char *prompt, uint64_t *res,
152 uint64_t low, uint64_t up, int *expsize);
8460875d 153
83fa0f80 154static int ui_enabled;
502c5186
KZ
155static volatile sig_atomic_t sig_resize;
156static volatile sig_atomic_t sig_die;
8c3a5a44 157
eef63970
KZ
158/* ncurses LINES and COLS may be actual variables or *macros*, but we need
159 * something portable and writable */
2ba641e5
SK
160static size_t ui_lines;
161static size_t ui_cols;
eef63970 162
88942179 163/* menu item */
3b411726 164struct cfdisk_menuitem {
8c3a5a44
KZ
165 int key; /* keyboard shortcut */
166 const char *name; /* item name */
88942179 167 const char *desc; /* item description (hint) */
6fed9601 168 void *userdata;
749af4b6
KZ
169};
170
88942179 171/* menu */
8c3a5a44 172struct cfdisk_menu {
88942179
KZ
173 char *title; /* optional menu title */
174 struct cfdisk_menuitem *items; /* array with menu items */
175 char *ignore;/* string with keys to ignore */
176 size_t width; /* maximal width of the menu item */
177 size_t nitems; /* number of the active menu items */
178 size_t page_sz;/* when menu longer than screen */
179 size_t idx; /* the current menu item */
b5ef65a9 180 int prefkey;/* preferred menu item */
8c3a5a44 181 struct cfdisk_menu *prev;
88942179
KZ
182
183 /* @ignore keys generator */
63128bbf 184 int (*ignore_cb) (struct cfdisk *, char *, size_t);
8a726114 185
88942179 186 unsigned int vertical : 1; /* enable vertical mode */
749af4b6
KZ
187};
188
88942179 189/* main menu */
3b411726 190static struct cfdisk_menuitem main_menuitems[] = {
8c3a5a44
KZ
191 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
192 { 'd', N_("Delete"), N_("Delete the current partition") },
a351f2e5 193 { 'r', N_("Resize"), N_("Reduce or enlarge the current partition") },
8c3a5a44 194 { 'n', N_("New"), N_("Create new partition from free space") },
3d6db3fd 195 { 'q', N_("Quit"), N_("Quit program without writing changes") },
8c3a5a44 196 { 't', N_("Type"), N_("Change the partition type") },
314a0dd8 197 { 'h', N_("Help"), N_("Print help screen") },
d5314bf5 198 { 's', N_("Sort"), N_("Fix partitions order") },
8c3a5a44 199 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
a89eafed 200 { 'u', N_("Dump"), N_("Dump partition table to sfdisk compatible script file") },
8c3a5a44
KZ
201 { 0, NULL, NULL }
202};
6dbe3af9 203
dda7fe12
OO
204/* extra partinfo in name:value pairs */
205struct cfdisk_extra {
206 char *name;
207 char *data;
208
209 struct list_head exs;
210};
211
212/* line and extra partinfo list_head */
213struct cfdisk_line {
214 char *data; /* line data */
215 struct libscols_table *extra; /* extra info ('X') */
216 WINDOW *w; /* window with extra info */
217};
218
88942179 219/* top level control struct */
8c3a5a44
KZ
220struct cfdisk {
221 struct fdisk_context *cxt; /* libfdisk context */
222 struct fdisk_table *table; /* partition table */
044ebc0c 223 struct fdisk_table *original_layout; /* original on-disk PT */
dda7fe12 224
a6f69126 225 struct cfdisk_menu *menu; /* the current menu */
7eda085c 226
bd85d11f
KZ
227 int *fields; /* output columns IDs */
228 size_t nfields; /* number of columns IDs */
7eda085c 229
8c3a5a44
KZ
230 char *linesbuf; /* table as string */
231 size_t linesbufsz; /* size of the tb_buf */
7eda085c 232
dda7fe12
OO
233 struct cfdisk_line *lines; /* list of lines */
234
8c3a5a44 235 size_t nlines; /* number of lines */
a6f69126
KZ
236 size_t lines_idx; /* current line <0..N>, exclude header */
237 size_t page_sz;
d5314bf5 238
bc787727
KZ
239 unsigned int nwrites; /* fdisk_write_disklabel() counter */
240
dda7fe12
OO
241 WINDOW *act_win; /* the window currently on the screen */
242
243#ifdef HAVE_LIBMOUNT
244 struct libmnt_table *mtab;
245 struct libmnt_table *fstab;
246 struct libmnt_cache *mntcache;
247#endif
d121efdb 248 unsigned int wrong_order :1, /* PT not in right order */
dda7fe12 249 zero_start :1, /* ignore existing partition table */
044ebc0c 250 device_is_used : 1, /* don't use re-read ioctl */
dda7fe12 251 show_extra :1; /* show extra partinfo */
8c3a5a44 252};
5c36a0eb 253
14620822
KZ
254
255/*
256 * let's use include/debug.h stuff for cfdisk too
257 */
2ba641e5 258static UL_DEBUG_DEFINE_MASK(cfdisk);
819d9a29 259UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
14620822
KZ
260
261#define CFDISK_DEBUG_INIT (1 << 1)
262#define CFDISK_DEBUG_UI (1 << 2)
263#define CFDISK_DEBUG_MENU (1 << 3)
264#define CFDISK_DEBUG_MISC (1 << 4)
265#define CFDISK_DEBUG_TABLE (1 << 5)
266#define CFDISK_DEBUG_ALL 0xFFFF
267
14620822
KZ
268#define DBG(m, x) __UL_DBG(cfdisk, CFDISK_DEBUG_, m, x)
269
270static void cfdisk_init_debug(void)
271{
a15dca2f 272 __UL_INIT_DEBUG_FROM_ENV(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
14620822
KZ
273}
274
bd85d11f 275/* Initialize output columns -- we follow libfdisk fields (usually specific
88942179
KZ
276 * to the label type.
277 */
8c3a5a44
KZ
278static int cols_init(struct cfdisk *cf)
279{
280 assert(cf);
5c36a0eb 281
bd85d11f
KZ
282 free(cf->fields);
283 cf->fields = NULL;
284 cf->nfields = 0;
5c36a0eb 285
11ee1177 286 return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
5c36a0eb
KZ
287}
288
502c5186
KZ
289static void die_on_signal(void)
290{
291 DBG(MISC, ul_debug("die on signal."));
292 ui_end();
293 exit(EXIT_FAILURE);
294}
295
7556c944
KZ
296static void resize(void)
297{
298 struct winsize ws;
299
300 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1
301 && ws.ws_row && ws.ws_col) {
eef63970
KZ
302 ui_lines = ws.ws_row;
303 ui_cols = ws.ws_col;
7556c944
KZ
304#if HAVE_RESIZETERM
305 resizeterm(ws.ws_row, ws.ws_col);
306#endif
307 clearok(stdscr, TRUE);
308 }
309 touchwin(stdscr);
310
eef63970
KZ
311 DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
312 ui_cols, ui_lines));
c431d901 313 sig_resize = 0;
7556c944
KZ
314}
315
c743bf74
KZ
316/* Reads partition in tree-like order from scols
317 */
318static int partition_from_scols(struct fdisk_table *tb,
319 struct libscols_line *ln)
320{
321 struct fdisk_partition *pa = scols_line_get_userdata(ln);
322
323 fdisk_table_add_partition(tb, pa);
324 fdisk_unref_partition(pa);
325
326 if (scols_line_has_children(ln)) {
327 struct libscols_line *chln;
328 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
329
330 if (!itr)
331 return -EINVAL;
332 while (scols_line_next_child(ln, itr, &chln) == 0)
333 partition_from_scols(tb, chln);
a9408274 334 scols_free_iter(itr);
c743bf74
KZ
335 }
336 return 0;
337}
338
8c3a5a44
KZ
339static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
340{
8903f7df 341 struct fdisk_partition *pa;
8c3a5a44 342 struct fdisk_label *lb;
1b504263 343 struct fdisk_iter *itr;
baa3b270
OO
344 struct libscols_table *table = NULL;
345 struct libscols_iter *s_itr = NULL;
8c3a5a44 346 char *res = NULL;
8903f7df 347 size_t i;
45333e9d 348 int tree = 0;
baa3b270 349 struct libscols_line *ln, *ln_cont = NULL;
8c3a5a44 350
14620822 351 DBG(TABLE, ul_debug("convert to string"));
8c3a5a44
KZ
352
353 assert(cf);
354 assert(cf->cxt);
bd85d11f 355 assert(cf->fields);
8c3a5a44
KZ
356 assert(tb);
357
6a632136 358 lb = fdisk_get_label(cf->cxt, NULL);
8c3a5a44
KZ
359 assert(lb);
360
8c3a5a44
KZ
361 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
362 if (!itr)
363 goto done;
5c36a0eb 364
45333e9d
KZ
365 /* get container (e.g. extended partition) */
366 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
367 if (fdisk_partition_is_nested(pa)) {
14620822 368 DBG(TABLE, ul_debug("nested detected, using tree"));
baa3b270 369 tree = SCOLS_FL_TREE;
45333e9d
KZ
370 break;
371 }
372 }
45333e9d 373
0925a9dd 374 table = scols_new_table();
baa3b270 375 if (!table)
45333e9d 376 goto done;
0925a9dd 377 scols_table_enable_maxout(table, 1);
717fff86 378 scols_table_enable_nowrap(table, 1);
45333e9d 379
9907a95a
KZ
380#if !defined(HAVE_LIBNCURSESW) || !defined(HAVE_WIDECHAR)
381 scols_table_enable_ascii(table, 1);
382#endif
383
8c3a5a44 384 /* headers */
bd85d11f
KZ
385 for (i = 0; i < cf->nfields; i++) {
386 int fl = 0;
387 const struct fdisk_field *field =
388 fdisk_label_get_field(lb, cf->fields[i]);
389 if (!field)
390 continue;
391
392 if (fdisk_field_is_number(field))
393 fl |= SCOLS_FL_RIGHT;
394 if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
395 fl |= SCOLS_FL_TRUNC;
396 if (tree && fdisk_field_get_id(field) == FDISK_FIELD_DEVICE)
397 fl |= SCOLS_FL_TREE;
398
399 if (!scols_table_new_column(table,
b6e3c614 400 _(fdisk_field_get_name(field)),
bd85d11f
KZ
401 fdisk_field_get_width(field), fl))
402 goto done;
8c3a5a44
KZ
403 }
404
405 /* data */
8903f7df
KZ
406 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
407
8c3a5a44 408 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
baa3b270 409 struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
8903f7df 410
baa3b270 411 ln = scols_table_new_line(table, parent);
8c3a5a44
KZ
412 if (!ln)
413 goto done;
bd85d11f 414 for (i = 0; i < cf->nfields; i++) {
8c3a5a44 415 char *cdata = NULL;
bd85d11f 416
d44115f3 417 if (fdisk_partition_to_string(pa, cf->cxt,
bd85d11f 418 cf->fields[i], &cdata))
8c3a5a44 419 continue;
c743bf74 420 scols_line_refer_data(ln, i, cdata);
8c3a5a44 421 }
8903f7df 422 if (tree && fdisk_partition_is_container(pa))
45333e9d 423 ln_cont = ln;
d051ea93 424
baa3b270 425 scols_line_set_userdata(ln, (void *) pa);
d051ea93 426 fdisk_ref_partition(pa);
8c3a5a44 427 }
6dbe3af9 428
baa3b270 429 if (scols_table_is_empty(table))
d051ea93
KZ
430 goto done;
431
baa3b270
OO
432 scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
433 scols_print_table_to_string(table, &res);
d051ea93 434
baa3b270 435 /* scols_* code might reorder lines, let's reorder @tb according to the
d051ea93
KZ
436 * final output (it's no problem because partitions are addressed by
437 * parno stored within struct fdisk_partition) */
438
439 /* remove all */
49e1a333 440 fdisk_reset_table(tb);
d051ea93 441
baa3b270
OO
442 s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
443 if (!s_itr)
444 goto done;
445
c743bf74 446 /* add all in the right order (don't forget the output is tree) */
baa3b270 447 while (scols_table_next_line(table, s_itr, &ln) == 0) {
c743bf74
KZ
448 if (scols_line_get_parent(ln))
449 continue;
450 if (partition_from_scols(tb, ln))
451 break;
8c3a5a44
KZ
452 }
453done:
baa3b270
OO
454 scols_unref_table(table);
455 scols_free_iter(s_itr);
8c3a5a44 456 fdisk_free_iter(itr);
6dbe3af9 457
8c3a5a44 458 return res;
2b6fc908
KZ
459}
460
dda7fe12
OO
461static void cfdisk_free_lines(struct cfdisk *cf)
462{
463 size_t i = 0;
464 while(i < cf->nlines) {
465 scols_unref_table(cf->lines[i].extra);
466
467 DBG(UI, ul_debug("delete window: %p",
468 cf->lines[i].w));
469
a2d0dbb4
KZ
470 if (cf->lines[i].w)
471 delwin(cf->lines[i].w);
472 cf->lines[i].w = NULL;
dda7fe12
OO
473 ++i;
474 }
475 cf->act_win = NULL;
476 free(cf->lines);
a2d0dbb4 477 cf->lines = NULL;
dda7fe12 478}
88942179
KZ
479/*
480 * Read data about partitions from libfdisk and prepare output lines.
481 */
8460875d 482static int lines_refresh(struct cfdisk *cf)
8c3a5a44
KZ
483{
484 int rc;
485 char *p;
486 size_t i;
7eda085c 487
8c3a5a44 488 assert(cf);
2b6fc908 489
14620822 490 DBG(TABLE, ul_debug("refreshing buffer"));
c64061c9 491
8c3a5a44 492 free(cf->linesbuf);
dda7fe12 493 cfdisk_free_lines(cf);
8c3a5a44
KZ
494 cf->linesbuf = NULL;
495 cf->linesbufsz = 0;
496 cf->lines = NULL;
497 cf->nlines = 0;
6dbe3af9 498
8c3a5a44 499 fdisk_unref_table(cf->table);
1af8003b 500 cf->table = NULL;
6dbe3af9 501
2cec7949
KZ
502 /* read partitions and free spaces into cf->table */
503 rc = fdisk_get_partitions(cf->cxt, &cf->table);
504 if (!rc)
505 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
8c3a5a44
KZ
506 if (rc)
507 return rc;
0d8589c5 508
8c3a5a44
KZ
509 cf->linesbuf = table_to_string(cf, cf->table);
510 if (!cf->linesbuf)
511 return -ENOMEM;
6dbe3af9 512
8c3a5a44
KZ
513 cf->linesbufsz = strlen(cf->linesbuf);
514 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
a6f69126 515 cf->page_sz = 0;
d5314bf5 516 cf->wrong_order = fdisk_table_wrong_order(cf->table) ? 1 : 0;
a6f69126
KZ
517
518 if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
519 cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
6dbe3af9 520
dda7fe12 521 cf->lines = xcalloc(cf->nlines, sizeof(struct cfdisk_line));
6dbe3af9 522
8c3a5a44 523 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
86688c37
KZ
524 cf->lines[i].data = p;
525 p = strchr(p, '\n');
8c3a5a44
KZ
526 if (p) {
527 *p = '\0';
528 p++;
529 }
dda7fe12
OO
530 cf->lines[i].extra = scols_new_table();
531 scols_table_enable_noheadings(cf->lines[i].extra, 1);
532 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_RIGHT);
533 scols_table_new_column(cf->lines[i].extra, NULL, 0, SCOLS_FL_TRUNC);
8c3a5a44 534 }
6dbe3af9 535
8c3a5a44 536 return 0;
6dbe3af9
KZ
537}
538
00b4f26a
KZ
539static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
540{
541 assert(cf);
542 assert(cf->table);
543
544 return fdisk_table_get_partition(cf->table, cf->lines_idx);
545}
546
45333e9d
KZ
547static int is_freespace(struct cfdisk *cf, size_t i)
548{
549 struct fdisk_partition *pa;
550
551 assert(cf);
552 assert(cf->table);
553
554 pa = fdisk_table_get_partition(cf->table, i);
555 return fdisk_partition_is_freespace(pa);
556}
557
b1f58330 558/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
9e930041 559 * response back to libfdisk
b1f58330
KZ
560 */
561static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
562{
3b411726 563 struct cfdisk_menuitem *d, *cm;
b1f58330
KZ
564 int key;
565 size_t i = 0, nitems;
566 const char *name, *desc;
567
568 assert(ask);
569 assert(cf);
570
571 /* create cfdisk menu according to libfdisk ask-menu, note that the
572 * last cm[] item has to be empty -- so nitems + 1 */
573 nitems = fdisk_ask_menu_get_nitems(ask);
3b411726 574 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
b1f58330
KZ
575
576 for (i = 0; i < nitems; i++) {
577 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
578 break;
579 cm[i].key = key;
580 cm[i].desc = desc;
581 cm[i].name = name;
582 }
583
584 /* make the new menu active */
63128bbf 585 menu_push(cf, cm);
b1f58330
KZ
586 ui_draw_menu(cf);
587 refresh();
588
589 /* wait for keys */
502c5186 590 while (!sig_die) {
7ee26cbf 591 key = getch();
ac27ea5c 592
502c5186
KZ
593 if (sig_die)
594 break;
c431d901 595 if (sig_resize)
7556c944 596 ui_menu_resize(cf);
ac27ea5c
KZ
597 if (ui_menu_move(cf, key) == 0)
598 continue;
599
600 switch (key) {
b1f58330
KZ
601 case KEY_ENTER:
602 case '\n':
603 case '\r':
6fed9601 604 d = menu_get_menuitem(cf, cf->menu->idx);
b1f58330
KZ
605 if (d)
606 fdisk_ask_menu_set_result(ask, d->key);
607 menu_pop(cf);
608 free(cm);
609 return 0;
610 }
502c5186
KZ
611 }
612
613 if (sig_die)
614 die_on_signal();
b1f58330
KZ
615
616 menu_pop(cf);
617 free(cm);
618 return -1;
619}
620
88942179
KZ
621/* libfdisk callback
622 */
ffab025d
KZ
623static int ask_callback(struct fdisk_context *cxt __attribute__((__unused__)),
624 struct fdisk_ask *ask,
625 void *data __attribute__((__unused__)))
8c3a5a44
KZ
626{
627 int rc = 0;
6dbe3af9 628
8c3a5a44 629 assert(ask);
6dbe3af9 630
8c3a5a44
KZ
631 switch(fdisk_ask_get_type(ask)) {
632 case FDISK_ASKTYPE_INFO:
83fa0f80 633 ui_info(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
634 break;
635 case FDISK_ASKTYPE_WARNX:
83fa0f80 636 ui_warnx(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
637 break;
638 case FDISK_ASKTYPE_WARN:
83fa0f80 639 ui_warn(fdisk_ask_print_get_mesg(ask));
8c3a5a44 640 break;
b1f58330
KZ
641 case FDISK_ASKTYPE_MENU:
642 ask_menu(ask, (struct cfdisk *) data);
643 break;
8c3a5a44 644 default:
83fa0f80
KZ
645 ui_warnx(_("internal error: unsupported dialog type %d"),
646 fdisk_ask_get_type(ask));
8c3a5a44
KZ
647 return -EINVAL;
648 }
649 return rc;
fd6b7a7f 650}
6dbe3af9 651
7aa0d529 652static int ui_end(void)
8c3a5a44 653{
7aa0d529 654 if (!ui_enabled)
8c3a5a44
KZ
655 return -EINVAL;
656
48d7b13a 657#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
eef63970 658 SLsmg_gotorc(ui_lines - 1, 0);
8c3a5a44 659 SLsmg_refresh();
2b6fc908 660#else
eef63970 661 mvcur(0, ui_cols - 1, ui_lines-1, 0);
2b6fc908 662#endif
cd8414f7 663 curs_set(1);
8c3a5a44
KZ
664 nl();
665 endwin();
666 printf("\n");
7aa0d529 667 ui_enabled = 0;
8c3a5a44 668 return 0;
6dbe3af9
KZ
669}
670
eef63970 671static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
f609e92e 672{
8c3a5a44 673 size_t width;
8c3a5a44 674 char *buf = NULL;
f609e92e 675
8c3a5a44
KZ
676 move(line, 0);
677 clrtoeol();
f609e92e 678
8c3a5a44 679 xvasprintf(&buf, fmt, ap);
fd6b7a7f 680
b1f58330 681 width = mbs_safe_width(buf);
eef63970
KZ
682 if (width > (size_t) ui_cols) {
683 char *p = strrchr(buf + ui_cols, ' ');
91ba41ca 684 if (!p)
eef63970 685 p = buf + ui_cols;
91ba41ca 686 *p = '\0';
eef63970 687 if (line + 1 >= ui_lines)
91ba41ca
KZ
688 line--;
689 attron(attrs);
690 mvaddstr(line, 0, buf);
691 mvaddstr(line + 1, 0, p+1);
692 attroff(attrs);
693 } else {
694 attron(attrs);
eef63970 695 mvaddstr(line, (ui_cols - width) / 2, buf);
91ba41ca
KZ
696 attroff(attrs);
697 }
8c3a5a44 698 free(buf);
6dbe3af9
KZ
699}
700
eef63970 701static void ui_center(size_t line, const char *fmt, ...)
1af8003b
KZ
702{
703 va_list ap;
704 va_start(ap, fmt);
705 ui_vprint_center(line, 0, fmt, ap);
706 va_end(ap);
707}
708
83fa0f80
KZ
709static void ui_warnx(const char *fmt, ...)
710{
711 va_list ap;
712 va_start(ap, fmt);
713 if (ui_enabled)
702d0e1c 714 ui_vprint_center(WARN_LINE,
f1512be8
KZ
715 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
716 fmt, ap);
8fc37981 717 else {
83fa0f80 718 vfprintf(stderr, fmt, ap);
8fc37981
KZ
719 fputc('\n', stderr);
720 }
83fa0f80
KZ
721 va_end(ap);
722}
723
724static void ui_warn(const char *fmt, ...)
1af8003b 725{
83fa0f80 726 char *fmt_m;
1af8003b 727 va_list ap;
83fa0f80
KZ
728
729 xasprintf(&fmt_m, "%s: %m", fmt);
730
1af8003b 731 va_start(ap, fmt);
83fa0f80 732 if (ui_enabled)
702d0e1c 733 ui_vprint_center(WARN_LINE,
f1512be8
KZ
734 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
735 fmt_m, ap);
8fc37981 736 else {
83fa0f80 737 vfprintf(stderr, fmt_m, ap);
8fc37981
KZ
738 fputc('\n', stderr);
739 }
1af8003b 740 va_end(ap);
83fa0f80 741 free(fmt_m);
1af8003b
KZ
742}
743
29e9b787
KZ
744static void ui_clean_warn(void)
745{
746 move(WARN_LINE, 0);
747 clrtoeol();
748}
749
7aa0d529
KZ
750static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
751 {
752 va_list ap;
753 ui_end();
754
755 va_start(ap, fmt);
756 fprintf(stderr, "%s: ", program_invocation_short_name);
757 vfprintf(stderr, fmt, ap);
8fc37981 758 fputc('\n', stderr);
7aa0d529
KZ
759 va_end(ap);
760
761 exit(rc);
762}
763
1af8003b
KZ
764static void ui_info(const char *fmt, ...)
765{
766 va_list ap;
767 va_start(ap, fmt);
83fa0f80 768 if (ui_enabled)
f9fb8290
KZ
769 ui_vprint_center(INFO_LINE,
770 colors_wanted() ? COLOR_PAIR(CFDISK_CL_INFO) : 0,
771 fmt, ap);
8fc37981 772 else {
83fa0f80 773 vfprintf(stdout, fmt, ap);
8fc37981
KZ
774 fputc('\n', stdout);
775 }
1af8003b
KZ
776 va_end(ap);
777}
778
779static void ui_clean_info(void)
780{
781 move(INFO_LINE, 0);
782 clrtoeol();
783}
6dbe3af9 784
b1f58330
KZ
785static void ui_hint(const char *fmt, ...)
786{
787 va_list ap;
788 va_start(ap, fmt);
789 if (ui_enabled)
790 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
8fc37981 791 else {
b1f58330 792 vfprintf(stdout, fmt, ap);
8fc37981
KZ
793 fputc('\n', stdout);
794 }
b1f58330
KZ
795 va_end(ap);
796}
797
798static void ui_clean_hint(void)
799{
800 move(HINT_LINE, 0);
801 clrtoeol();
802}
803
502c5186
KZ
804
805static void sig_handler_die(int dummy __attribute__((__unused__)))
8c3a5a44 806{
502c5186 807 sig_die = 1;
6dbe3af9
KZ
808}
809
c431d901 810static void sig_handler_resize(int dummy __attribute__((__unused__)))
7556c944 811{
c431d901 812 sig_resize = 1;
7556c944
KZ
813}
814
815static void menu_refresh_size(struct cfdisk *cf)
816{
817 if (cf->menu && cf->menu->nitems)
eef63970 818 cf->menu->page_sz = (cf->menu->nitems / (ui_lines - 4)) ? ui_lines - 4 : 0;
7556c944
KZ
819}
820
8c3a5a44
KZ
821static void menu_update_ignore(struct cfdisk *cf)
822{
00b4f26a
KZ
823 char ignore[128] = { 0 };
824 int i = 0;
8c3a5a44 825 struct cfdisk_menu *m;
b5ef65a9 826 struct cfdisk_menuitem *d, *org = NULL;
83fa0f80 827 size_t idx;
6dbe3af9 828
8c3a5a44 829 assert(cf);
63128bbf
KZ
830 assert(cf->menu);
831 assert(cf->menu->ignore_cb);
6dbe3af9 832
8c3a5a44 833 m = cf->menu;
14620822 834 DBG(MENU, ul_debug("update menu ignored keys"));
6dbe3af9 835
63128bbf 836 i = m->ignore_cb(cf, ignore, sizeof(ignore));
00b4f26a
KZ
837 ignore[i] = '\0';
838
8c3a5a44 839 /* return if no change */
b5ef65a9 840 if ((!m->ignore && !*ignore)
00b4f26a 841 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
8c3a5a44 842 return;
fd6b7a7f 843 }
6dbe3af9 844
b5ef65a9
KZ
845 if (!m->prefkey)
846 org = menu_get_menuitem(cf, m->idx);
847
8c3a5a44 848 free(m->ignore);
8460875d 849 m->ignore = xstrdup(ignore);
8c3a5a44 850 m->nitems = 0;
6dbe3af9 851
3b411726 852 for (d = m->items; d->name; d++) {
8c3a5a44 853 if (m->ignore && strchr(m->ignore, d->key))
8460875d
KZ
854 continue;
855 m->nitems++;
8c3a5a44 856 }
83fa0f80 857
b5ef65a9
KZ
858 DBG(MENU, ul_debug("update menu preferred keys"));
859
83fa0f80
KZ
860 /* refresh menu index to be at the same menuitem or go to the first */
861 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
6fed9601 862 m->idx = idx;
b5ef65a9
KZ
863 else if (m->prefkey && menu_get_menuitem_by_key(cf, m->prefkey, &idx))
864 m->idx = idx;
83fa0f80 865 else
6fed9601
KZ
866 m->idx = 0;
867
7556c944 868 menu_refresh_size(cf);
6dbe3af9
KZ
869}
870
b1f58330
KZ
871static struct cfdisk_menu *menu_push(
872 struct cfdisk *cf,
3b411726 873 struct cfdisk_menuitem *items)
8c3a5a44
KZ
874{
875 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
3b411726 876 struct cfdisk_menuitem *d;
6dbe3af9 877
8c3a5a44 878 assert(cf);
6dbe3af9 879
14620822 880 DBG(MENU, ul_debug("new menu"));
6dbe3af9 881
8c3a5a44 882 m->prev = cf->menu;
3b411726 883 m->items = items;
6dbe3af9 884
3b411726 885 for (d = m->items; d->name; d++) {
8c3a5a44 886 const char *name = _(d->name);
b1f58330 887 size_t len = mbs_safe_width(name);
8c3a5a44
KZ
888 if (len > m->width)
889 m->width = len;
890 m->nitems++;
891 }
6dbe3af9 892
8c3a5a44 893 cf->menu = m;
b5ef65a9 894
7556c944 895 menu_refresh_size(cf);
8c3a5a44 896 return m;
6dbe3af9
KZ
897}
898
8c3a5a44 899static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
6dbe3af9 900{
8c3a5a44 901 struct cfdisk_menu *m = NULL;
6dbe3af9 902
8c3a5a44 903 assert(cf);
7eda085c 904
14620822 905 DBG(MENU, ul_debug("pop menu"));
7eda085c 906
8c3a5a44
KZ
907 if (cf->menu) {
908 m = cf->menu->prev;
909 free(cf->menu->ignore);
8a726114 910 free(cf->menu->title);
8c3a5a44 911 free(cf->menu);
c07ebfa1 912 }
8c3a5a44
KZ
913 cf->menu = m;
914 return cf->menu;
6dbe3af9
KZ
915}
916
8a726114
KZ
917static void menu_set_title(struct cfdisk_menu *m, const char *title)
918{
919 char *str = NULL;
920
921 if (title) {
922 size_t len = mbs_safe_width(title);
5c04b408
KZ
923 if (len + MENU_TITLE_PADDING > m->width)
924 m->width = len + MENU_TITLE_PADDING;
8a726114
KZ
925 str = xstrdup(title);
926 }
927 m->title = str;
928}
929
930
b1f58330 931static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
8c3a5a44
KZ
932{
933 struct sigaction sa;
6dbe3af9 934
14620822 935 DBG(UI, ul_debug("init"));
6dbe3af9 936
8c3a5a44
KZ
937 /* setup SIGCHLD handler */
938 sigemptyset(&sa.sa_mask);
939 sa.sa_flags = 0;
502c5186 940 sa.sa_handler = sig_handler_die;
8c3a5a44
KZ
941 sigaction(SIGINT, &sa, NULL);
942 sigaction(SIGTERM, &sa, NULL);
6dbe3af9 943
c431d901 944 sa.sa_handler = sig_handler_resize;
7556c944
KZ
945 sigaction(SIGWINCH, &sa, NULL);
946
83fa0f80 947 ui_enabled = 1;
8c3a5a44 948 initscr();
6dbe3af9 949
f1512be8 950#ifdef HAVE_USE_DEFAULT_COLORS
04915c3f 951 if (colors_wanted() && has_colors()) {
1af8003b
KZ
952 size_t i;
953
954 start_color();
955 use_default_colors();
1af8003b
KZ
956 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
957 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
958 }
f1512be8 959#else
e66a6627 960 colors_off();
f1512be8 961#endif
1af8003b 962
8c3a5a44
KZ
963 cbreak();
964 noecho();
965 nonl();
966 curs_set(0);
967 keypad(stdscr, TRUE);
6dbe3af9 968
8c3a5a44 969 return 0;
7eda085c
KZ
970}
971
5c04b408
KZ
972/* "[ string ]" */
973#define MENU_H_ITEMWIDTH(m) ( MENU_H_PRESTR_SZ \
974 + MENU_H_SPADDING \
975 + (m)->width \
976 + MENU_H_SPADDING \
977 + MENU_H_POSTSTR_SZ)
978
979#define MENU_V_ITEMWIDTH(m) (MENU_V_SPADDING + (m)->width + MENU_V_SPADDING)
980
981
8c3a5a44
KZ
982static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
983{
6fed9601
KZ
984 struct cfdisk_menu *m = cf->menu;
985
986 if (m->vertical) {
987 if (!m->page_sz) /* small menu */
eef63970 988 return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
6fed9601 989 return (idx % m->page_sz) + 1;
042f62df
RP
990 }
991
992 {
5c04b408 993 size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
eef63970 994 size_t items = ui_cols / len; /* items per line */
8a726114 995
a1da27a8
KZ
996 if (items == 0)
997 return 0;
8a726114
KZ
998 return MENU_START_LINE + ((idx / items));
999 }
7eda085c
KZ
1000}
1001
8c3a5a44
KZ
1002static int menuitem_get_column(struct cfdisk *cf, size_t idx)
1003{
8a726114 1004 if (cf->menu->vertical) {
5c04b408 1005 size_t nc = MENU_V_ITEMWIDTH(cf->menu);
eef63970 1006 if ((size_t) ui_cols <= nc)
7aa0d529 1007 return 0;
eef63970 1008 return (ui_cols - nc) / 2;
042f62df
RP
1009 }
1010
1011 {
5c04b408 1012 size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
eef63970 1013 size_t items = ui_cols / len; /* items per line */
8a726114 1014 size_t extra = items < cf->menu->nitems ? /* extra space on line */
eef63970
KZ
1015 ui_cols % len : /* - multi-line menu */
1016 ui_cols - (cf->menu->nitems * len); /* - one line menu */
8a726114 1017
a1da27a8
KZ
1018 if (items == 0)
1019 return 0; /* hmm... no space */
1020
5c04b408 1021 extra += MENU_H_BETWEEN; /* add padding after last item to extra */
e66ac5d3 1022
8a726114
KZ
1023 if (idx < items)
1024 return (idx * len) + (extra / 2);
1025 return ((idx % items) * len) + (extra / 2);
1026 }
df1dddf9
KZ
1027}
1028
6fed9601
KZ
1029static int menuitem_on_page(struct cfdisk *cf, size_t idx)
1030{
1031 struct cfdisk_menu *m = cf->menu;
1032
1033 if (m->page_sz == 0 ||
1034 m->idx / m->page_sz == idx / m->page_sz)
1035 return 1;
1036 return 0;
1037}
1038
3b411726 1039static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
8c3a5a44 1040{
3b411726 1041 struct cfdisk_menuitem *d;
8c3a5a44 1042 size_t i;
7eda085c 1043
3b411726 1044 for (i = 0, d = cf->menu->items; d->name; d++) {
8c3a5a44
KZ
1045 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1046 continue;
1047 if (i++ == idx)
1048 return d;
d26aa358 1049 }
7eda085c 1050
8c3a5a44 1051 return NULL;
6dbe3af9
KZ
1052}
1053
3b411726 1054static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
83fa0f80
KZ
1055 int key, size_t *idx)
1056{
3b411726 1057 struct cfdisk_menuitem *d;
83fa0f80 1058
3b411726 1059 for (*idx = 0, d = cf->menu->items; d->name; d++) {
83fa0f80
KZ
1060 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
1061 continue;
1062 if (key == d->key)
1063 return d;
1064 (*idx)++;
1065 }
1066
1067 return NULL;
1068}
1069
8c3a5a44 1070static void ui_draw_menuitem(struct cfdisk *cf,
3b411726 1071 struct cfdisk_menuitem *d,
8c3a5a44
KZ
1072 size_t idx)
1073{
e948f4b6 1074 char *buf, *ptr;
8c3a5a44 1075 const char *name;
5c04b408 1076 size_t width;
e948f4b6 1077 const size_t buf_sz = 80 * MB_CUR_MAX;
8a726114 1078 int ln, cl, vert = cf->menu->vertical;
8c3a5a44 1079
6fed9601
KZ
1080 if (!menuitem_on_page(cf, idx))
1081 return; /* no visible item */
1082 ln = menuitem_get_line(cf, idx);
1083 cl = menuitem_get_column(cf, idx);
1084
e948f4b6 1085 ptr = buf = xmalloc(buf_sz);
5c04b408
KZ
1086 /* string width */
1087 if (vert) {
1088 width = cf->menu->width + MENU_V_SPADDING;
1089 memset(ptr, ' ', MENU_V_SPADDING);
1090 ptr += MENU_V_SPADDING;
1091 } else
1092 width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
1093
8c3a5a44 1094 name = _(d->name);
e948f4b6 1095 mbsalign(name, ptr, buf_sz, &width,
8a726114
KZ
1096 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
1097 0);
8c3a5a44 1098
14620822 1099 DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
8c3a5a44
KZ
1100 cl, ln, buf));
1101
8a726114
KZ
1102 if (vert) {
1103 mvaddch(ln, cl - 1, ACS_VLINE);
5c04b408 1104 mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
8a726114
KZ
1105 }
1106
5c04b408 1107 if (cf->menu->idx == idx)
8c3a5a44 1108 standout();
5c04b408
KZ
1109
1110 if (vert)
1111 mvprintw(ln, cl, "%s", buf);
1112 else
1113 mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
e948f4b6 1114 free(buf);
5c04b408
KZ
1115
1116 if (cf->menu->idx == idx) {
8c3a5a44
KZ
1117 standend();
1118 if (d->desc)
b6e3c614 1119 ui_hint(_(d->desc));
5c04b408 1120 }
6dbe3af9
KZ
1121}
1122
7556c944
KZ
1123static void ui_clean_menu(struct cfdisk *cf)
1124{
1125 size_t i;
702d0e1c 1126 size_t lastline;
7556c944
KZ
1127 struct cfdisk_menu *m = cf->menu;
1128 size_t ln = menuitem_get_line(cf, 0);
1129
1130 if (m->vertical)
702d0e1c 1131 lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
7556c944 1132 else
702d0e1c 1133 lastline = menuitem_get_line(cf, m->nitems);
7556c944 1134
702d0e1c 1135 for (i = ln; i <= lastline; i++) {
7556c944
KZ
1136 move(i, 0);
1137 clrtoeol();
702d0e1c 1138 DBG(MENU, ul_debug("clean_menu: line %zu", i));
7556c944
KZ
1139 }
1140 if (m->vertical) {
1141 move(ln - 1, 0);
1142 clrtoeol();
1143 }
1144 ui_clean_hint();
1145}
1146
8c3a5a44
KZ
1147static void ui_draw_menu(struct cfdisk *cf)
1148{
3b411726 1149 struct cfdisk_menuitem *d;
8a726114 1150 struct cfdisk_menu *m;
6fed9601
KZ
1151 size_t i = 0;
1152 size_t ln = menuitem_get_line(cf, 0);
1153 size_t nlines;
7eda085c 1154
8c3a5a44
KZ
1155 assert(cf);
1156 assert(cf->menu);
fd6b7a7f 1157
14620822 1158 DBG(MENU, ul_debug("draw start"));
6dbe3af9 1159
7556c944 1160 ui_clean_menu(cf);
8a726114
KZ
1161 m = cf->menu;
1162
6fed9601
KZ
1163 if (m->vertical)
1164 nlines = m->page_sz ? m->page_sz : m->nitems;
1165 else
1166 nlines = menuitem_get_line(cf, m->nitems);
1167
63128bbf
KZ
1168 if (m->ignore_cb)
1169 menu_update_ignore(cf);
00b4f26a 1170 i = 0;
8c3a5a44
KZ
1171 while ((d = menu_get_menuitem(cf, i)))
1172 ui_draw_menuitem(cf, d, i++);
6dbe3af9 1173
8a726114 1174 if (m->vertical) {
8a726114 1175 size_t cl = menuitem_get_column(cf, 0);
6fed9601 1176 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
8a726114 1177
6fed9601 1178 /* corners and horizontal lines */
8a726114
KZ
1179 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
1180 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
1181
5c04b408 1182 for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
8a726114
KZ
1183 mvaddch(ln - 1, cl + i, ACS_HLINE);
1184 mvaddch(ln + nlines, cl + i, ACS_HLINE);
1185 }
6fed9601 1186
8a726114
KZ
1187 mvaddch(ln - 1, cl + i, ACS_URCORNER);
1188 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
1189
6fed9601
KZ
1190 /* draw also lines around empty lines on last page */
1191 if (m->page_sz &&
1192 m->nitems / m->page_sz == m->idx / m->page_sz) {
1193 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
1194 mvaddch(i, cl - 1, ACS_VLINE);
5c04b408 1195 mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
6fed9601
KZ
1196 }
1197 }
8a726114
KZ
1198 if (m->title) {
1199 attron(A_BOLD);
1200 mvprintw(ln - 1, cl, " %s ", m->title);
1201 attroff(A_BOLD);
1202 }
6fed9601 1203 if (curpg != 0)
5c04b408 1204 mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
6fed9601 1205 if (m->page_sz && curpg < m->nitems / m->page_sz)
5c04b408 1206 mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
8a726114
KZ
1207 }
1208
14620822 1209 DBG(MENU, ul_debug("draw end."));
6dbe3af9
KZ
1210}
1211
dda7fe12
OO
1212inline static int extra_insert_pair(struct cfdisk_line *l, const char *name, const char *data)
1213{
1214 struct libscols_line *lsl;
4c166c22 1215 int rc;
dda7fe12
OO
1216
1217 assert(l);
a2d0dbb4 1218 assert(l->extra);
dda7fe12 1219
13c551f2 1220 if (!data || !*data)
dda7fe12
OO
1221 return 0;
1222
1223 lsl = scols_table_new_line(l->extra, NULL);
1224 if (!lsl)
1225 return -ENOMEM;
1226
4c166c22
KZ
1227 rc = scols_line_set_data(lsl, 0, name);
1228 if (!rc)
1229 rc = scols_line_set_data(lsl, 1, data);
dda7fe12 1230
4c166c22 1231 return rc;
dda7fe12
OO
1232}
1233
cf9b302d
KZ
1234#ifndef HAVE_LIBMOUNT
1235static char *get_mountpoint( struct cfdisk *cf __attribute__((unused)),
1236 const char *tagname __attribute__((unused)),
1237 const char *tagdata __attribute__((unused)))
1238{
1239 return NULL;
1240}
1241#else
1242static char *get_mountpoint(struct cfdisk *cf, const char *tagname, const char *tagdata)
dda7fe12
OO
1243{
1244 struct libmnt_fs *fs = NULL;
1245 char *target = NULL;
1246 int mounted = 0;
1247
cf9b302d
KZ
1248 assert(tagname);
1249 assert(tagdata);
dda7fe12 1250
cf9b302d 1251 DBG(UI, ul_debug("asking for mountpoint [%s=%s]", tagname, tagdata));
dda7fe12
OO
1252
1253 if (!cf->mntcache)
1254 cf->mntcache = mnt_new_cache();
1255
1256 /* 1st try between mounted filesystems */
1257 if (!cf->mtab) {
1258 cf->mtab = mnt_new_table();
1259 if (cf->mtab) {
1260 mnt_table_set_cache(cf->mtab, cf->mntcache);
1261 mnt_table_parse_mtab(cf->mtab, NULL);
1262 }
1263 }
1264
1265 if (cf->mtab)
cf9b302d 1266 fs = mnt_table_find_tag(cf->mtab, tagname, tagdata, MNT_ITER_FORWARD);
dda7fe12
OO
1267
1268 /* 2nd try fstab */
1269 if (!fs) {
1270 if (!cf->fstab) {
1271 cf->fstab = mnt_new_table();
1272 if (cf->fstab) {
1273 mnt_table_set_cache(cf->fstab, cf->mntcache);
a8955bf0
KZ
1274 if (mnt_table_parse_fstab(cf->fstab, NULL) != 0) {
1275 mnt_unref_table(cf->fstab);
1276 cf->fstab = NULL;
1277 }
dda7fe12
OO
1278 }
1279 }
1280 if (cf->fstab)
cf9b302d 1281 fs = mnt_table_find_tag(cf->fstab, tagname, tagdata, MNT_ITER_FORWARD);
dda7fe12
OO
1282 } else
1283 mounted = 1;
1284
1285 if (fs) {
1286 if (mounted)
1287 xasprintf(&target, _("%s (mounted)"), mnt_fs_get_target(fs));
1288 else
1289 target = xstrdup(mnt_fs_get_target(fs));
1290 }
1291
1292 return target;
1293}
1294#endif /* HAVE_LIBMOUNT */
1295
1296static void extra_prepare_data(struct cfdisk *cf)
1297{
1298 struct fdisk_partition *pa = get_current_partition(cf);
1299 struct cfdisk_line *l = &cf->lines[cf->lines_idx];
1300 char *data = NULL;
cf9b302d 1301 char *mountpoint = NULL;
dda7fe12
OO
1302
1303 DBG(UI, ul_debug("preparing extra data"));
1304
1305 /* string data should not equal an empty string */
1306 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_NAME, &data) && data) {
1307 extra_insert_pair(l, _("Partition name:"), data);
cf9b302d
KZ
1308 if (!mountpoint)
1309 mountpoint = get_mountpoint(cf, "PARTLABEL", data);
dda7fe12
OO
1310 free(data);
1311 }
1312
1313 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_UUID, &data) && data) {
1314 extra_insert_pair(l, _("Partition UUID:"), data);
cf9b302d
KZ
1315 if (!mountpoint)
1316 mountpoint = get_mountpoint(cf, "PARTUUID", data);
dda7fe12
OO
1317 free(data);
1318 }
1319
1320 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPE, &data) && data) {
1321 char *code = NULL, *type = NULL;
1322
1323 fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_TYPEID, &code);
1324 xasprintf(&type, "%s (%s)", data, code);
1325
1326 extra_insert_pair(l, _("Partition type:"), type);
1327 free(data);
1328 free(code);
1329 free(type);
1330 }
1331
1332 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_ATTR, &data) && data) {
1333 extra_insert_pair(l, _("Attributes:"), data);
1334 free(data);
1335 }
1336
1337 /* for numeric data, only show non-zero rows */
1338 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_BSIZE, &data) && data) {
1339 if (atoi(data))
1340 extra_insert_pair(l, "BSIZE:", data);
1341 free(data);
1342 }
1343
1344 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_CPG, &data) && data) {
1345 if (atoi(data))
1346 extra_insert_pair(l, "CPG:", data);
1347 free(data);
1348 }
1349
1350 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSIZE, &data) && data) {
1351 if (atoi(data))
1352 extra_insert_pair(l, "FSIZE:", data);
1353 free(data);
1354 }
1355
3d6db3fd
KZ
1356 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSUUID, &data) && data) {
1357 extra_insert_pair(l, _("Filesystem UUID:"), data);
cf9b302d
KZ
1358 if (!mountpoint)
1359 mountpoint = get_mountpoint(cf, "UUID", data);
3d6db3fd
KZ
1360 free(data);
1361 }
dda7fe12 1362
3d6db3fd
KZ
1363 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSLABEL, &data) && data) {
1364 extra_insert_pair(l, _("Filesystem LABEL:"), data);
cf9b302d
KZ
1365 if (!mountpoint)
1366 mountpoint = get_mountpoint(cf, "LABEL", data);
3d6db3fd
KZ
1367 free(data);
1368 }
1369 if (!fdisk_partition_to_string(pa, cf->cxt, FDISK_FIELD_FSTYPE, &data) && data) {
1370 extra_insert_pair(l, _("Filesystem:"), data);
1371 free(data);
dda7fe12 1372 }
dda7fe12 1373
cf9b302d
KZ
1374 if (mountpoint) {
1375 extra_insert_pair(l, _("Mountpoint:"), mountpoint);
1376 free(mountpoint);
dda7fe12 1377 }
dda7fe12
OO
1378}
1379
dda7fe12
OO
1380static int ui_draw_extra(struct cfdisk *cf)
1381{
1382 WINDOW *win_ex;
1383 int wline = 1;
1384 struct cfdisk_line *ln = &cf->lines[cf->lines_idx];
1385 char *tbstr = NULL, *end;
1386 int win_ex_start_line, win_height, tblen;
1387 int ndatalines;
1388
3542fc51
KZ
1389 if (!cf->show_extra)
1390 return 0;
1391
1392 DBG(UI, ul_debug("draw extra"));
1393
a2d0dbb4
KZ
1394 assert(ln->extra);
1395
cbcafba2 1396 if (cf->act_win) {
3542fc51 1397 wclear(cf->act_win);
cbcafba2
KZ
1398 touchwin(stdscr);
1399 }
3542fc51 1400
a2d0dbb4 1401 if (scols_table_is_empty(ln->extra)) {
dda7fe12 1402 extra_prepare_data(cf);
a2d0dbb4
KZ
1403 if (scols_table_is_empty(ln->extra))
1404 return 0;
1405 }
dda7fe12
OO
1406
1407 ndatalines = fdisk_table_get_nents(cf->table) + 1;
1408
1409 /* nents + header + one free line */
1410 win_ex_start_line = TABLE_START_LINE + ndatalines;
1411 win_height = MENU_START_LINE - win_ex_start_line;
1412 tblen = scols_table_get_nlines(ln->extra);
1413
1414 /* we can't get a single line of data under the partlist*/
1415 if (win_height < 3)
1416 return 1;
1417
1418 /* number of data lines + 2 for top/bottom lines */
1419 win_height = win_height < tblen + 2 ? win_height : tblen + 2;
1420
1421 if ((size_t) win_ex_start_line + win_height + 1 < MENU_START_LINE)
1422 win_ex_start_line = MENU_START_LINE - win_height;
1423
3542fc51 1424 win_ex = subwin(stdscr, win_height, ui_cols - 2, win_ex_start_line, 1);
dda7fe12
OO
1425
1426 scols_table_reduce_termwidth(ln->extra, 4);
1427 scols_print_table_to_string(ln->extra, &tbstr);
1428
1429 end = tbstr;
1430 while ((end = strchr(end, '\n')))
1431 *end++ = '\0';
1432
1433 box(win_ex, 0, 0);
1434
1435 end = tbstr;
1436 while (--win_height > 1) {
1437 mvwaddstr(win_ex, wline++, 1 /* window column*/, tbstr);
1438 tbstr += strlen(tbstr) + 1;
1439 }
1440 free(end);
1441
a2d0dbb4
KZ
1442 if (ln->w)
1443 delwin(ln->w);
dda7fe12
OO
1444
1445 DBG(UI, ul_debug("draw window: %p", win_ex));
3542fc51 1446 touchwin(stdscr);
dda7fe12
OO
1447 wrefresh(win_ex);
1448
1449 cf->act_win = ln->w = win_ex;
1450 return 0;
1451}
1452
8c3a5a44
KZ
1453static void ui_menu_goto(struct cfdisk *cf, int where)
1454{
3b411726 1455 struct cfdisk_menuitem *d;
8c3a5a44
KZ
1456 size_t old;
1457
f019ce1f
KZ
1458 /* stop and begin/end for vertical menus */
1459 if (cf->menu->vertical) {
1460 if (where < 0)
1461 where = 0;
1462 else if (where > (int) cf->menu->nitems - 1)
1463 where = cf->menu->nitems - 1;
1464 } else {
1465 /* continue from begin/end */
1466 if (where < 0)
1467 where = cf->menu->nitems - 1;
1468 else if ((size_t) where > cf->menu->nitems - 1)
1469 where = 0;
1470 }
6fed9601 1471 if ((size_t) where == cf->menu->idx)
8c3a5a44 1472 return;
6dbe3af9 1473
1af8003b
KZ
1474 ui_clean_info();
1475
6fed9601
KZ
1476 old = cf->menu->idx;
1477 cf->menu->idx = where;
1478
1479 if (!menuitem_on_page(cf, old)) {
1480 ui_draw_menu(cf);
1481 return;
1482 }
6dbe3af9 1483
8c3a5a44
KZ
1484 d = menu_get_menuitem(cf, old);
1485 ui_draw_menuitem(cf, d, old);
6dbe3af9 1486
8c3a5a44
KZ
1487 d = menu_get_menuitem(cf, where);
1488 ui_draw_menuitem(cf, d, where);
dda7fe12 1489
6dbe3af9
KZ
1490}
1491
ac27ea5c
KZ
1492static int ui_menu_move(struct cfdisk *cf, int key)
1493{
f019ce1f
KZ
1494 struct cfdisk_menu *m;
1495
ac27ea5c
KZ
1496 assert(cf);
1497 assert(cf->menu);
1498
c71f5a56 1499 if (key == (int) ERR)
7556c944
KZ
1500 return 0; /* ignore errors */
1501
f019ce1f
KZ
1502 m = cf->menu;
1503
14620822 1504 DBG(MENU, ul_debug("menu move key >%c<.", key));
f019ce1f
KZ
1505
1506 if (m->vertical)
ac27ea5c
KZ
1507 {
1508 switch (key) {
1509 case KEY_DOWN:
1510 case '\016': /* ^N */
1511 case 'j': /* Vi-like alternative */
f019ce1f 1512 ui_menu_goto(cf, m->idx + 1);
ac27ea5c
KZ
1513 return 0;
1514 case KEY_UP:
1515 case '\020': /* ^P */
1516 case 'k': /* Vi-like alternative */
f019ce1f 1517 ui_menu_goto(cf, (int) m->idx - 1);
ac27ea5c 1518 return 0;
f019ce1f
KZ
1519 case KEY_PPAGE:
1520 if (m->page_sz) {
1521 ui_menu_goto(cf, (int) m->idx - m->page_sz);
1522 return 0;
1523 }
d02c2035 1524 /* fallthrough */
ac27ea5c
KZ
1525 case KEY_HOME:
1526 ui_menu_goto(cf, 0);
1527 return 0;
f019ce1f
KZ
1528 case KEY_NPAGE:
1529 if (m->page_sz) {
1530 ui_menu_goto(cf, m->idx + m->page_sz);
1531 return 0;
1532 }
d02c2035 1533 /* fallthrough */
ac27ea5c 1534 case KEY_END:
f019ce1f 1535 ui_menu_goto(cf, m->nitems);
ac27ea5c
KZ
1536 return 0;
1537 }
1538 } else {
1539 switch (key) {
1540 case KEY_RIGHT:
1541 case '\t':
f019ce1f 1542 ui_menu_goto(cf, m->idx + 1);
ac27ea5c
KZ
1543 return 0;
1544 case KEY_LEFT:
1545#ifdef KEY_BTAB
1546 case KEY_BTAB:
1547#endif
f019ce1f 1548 ui_menu_goto(cf, (int) m->idx - 1);
ac27ea5c
KZ
1549 return 0;
1550 }
1551 }
1552
2f4eb047
KZ
1553 if (key == '\014') { /* ^L refresh */
1554 ui_menu_resize(cf);
1555 return 0;
1556 }
1557
b607e5fa 1558 DBG(MENU, ul_debug(" no menu move key"));
2f4eb047 1559 return 1;
ac27ea5c
KZ
1560}
1561
7556c944
KZ
1562/* but don't call me from ui_run(), this is for pop-up menus only */
1563static void ui_menu_resize(struct cfdisk *cf)
1564{
b607e5fa 1565 DBG(MENU, ul_debug("menu resize/refresh"));
7556c944
KZ
1566 resize();
1567 ui_clean_menu(cf);
1568 menu_refresh_size(cf);
1569 ui_draw_menu(cf);
1570 refresh();
1571}
1572
a6f69126
KZ
1573static int partition_on_page(struct cfdisk *cf, size_t i)
1574{
1575 if (cf->page_sz == 0 ||
1576 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1577 return 1;
1578 return 0;
1579}
1580
8c3a5a44
KZ
1581static void ui_draw_partition(struct cfdisk *cf, size_t i)
1582{
1583 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1584 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
a6f69126
KZ
1585 int cur = cf->lines_idx == i;
1586 size_t curpg = 0;
1587
1588 if (cf->page_sz) {
1589 if (!partition_on_page(cf, i))
1590 return;
1591 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1592 curpg = cf->lines_idx / cf->page_sz;
1593 }
6dbe3af9 1594
14620822
KZ
1595 DBG(UI, ul_debug(
1596 "draw partition %zu [page_sz=%zu, "
a6f69126
KZ
1597 "line=%d, idx=%zu]",
1598 i, cf->page_sz, ln, cf->lines_idx));
6dbe3af9 1599
a6f69126
KZ
1600 if (cur) {
1601 attron(A_REVERSE);
8c3a5a44 1602 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
dda7fe12 1603 mvaddstr(ln, cl, cf->lines[i + 1].data);
a6f69126 1604 attroff(A_REVERSE);
6dbe3af9 1605 } else {
45333e9d
KZ
1606 int at = 0;
1607
f1512be8 1608 if (colors_wanted() && is_freespace(cf, i)) {
45333e9d
KZ
1609 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1610 at = 1;
1611 }
8c3a5a44 1612 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
dda7fe12 1613 mvaddstr(ln, cl, cf->lines[i + 1].data);
45333e9d 1614 if (at)
dda7fe12 1615 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
6dbe3af9
KZ
1616 }
1617
a6f69126
KZ
1618 if ((size_t) ln == MENU_START_LINE - 1 &&
1619 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1620 if (cur)
1621 attron(A_REVERSE);
eef63970 1622 mvaddch(ln, ui_cols - 1, ACS_DARROW);
a6f69126
KZ
1623 mvaddch(ln, 0, ACS_DARROW);
1624 if (cur)
1625 attroff(A_REVERSE);
1626 }
dda7fe12 1627
6dbe3af9
KZ
1628}
1629
8c3a5a44
KZ
1630static int ui_draw_table(struct cfdisk *cf)
1631{
1632 int cl = ARROW_CURSOR_WIDTH;
1633 size_t i, nparts = fdisk_table_get_nents(cf->table);
a6f69126 1634 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
7eda085c 1635
14620822 1636 DBG(UI, ul_debug("draw table"));
6dbe3af9 1637
a6f69126
KZ
1638 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1639 move(i, 0);
1640 clrtoeol();
1641 }
6dbe3af9 1642
a2d0dbb4 1643 if (nparts == 0 || (size_t) cf->lines_idx > nparts - 1)
b2301179
KZ
1644 cf->lines_idx = nparts ? nparts - 1 : 0;
1645
8c3a5a44
KZ
1646 /* print header */
1647 attron(A_BOLD);
dda7fe12 1648 mvaddstr(TABLE_START_LINE, cl, cf->lines[0].data);
8c3a5a44 1649 attroff(A_BOLD);
6dbe3af9 1650
8c3a5a44
KZ
1651 /* print partitions */
1652 for (i = 0; i < nparts; i++)
1653 ui_draw_partition(cf, i);
6dbe3af9 1654
a6f69126 1655 if (curpg != 0) {
eef63970 1656 mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
a6f69126
KZ
1657 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1658 }
1659 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
eef63970 1660 mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
a6f69126
KZ
1661 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1662 }
8c3a5a44 1663 return 0;
6dbe3af9
KZ
1664}
1665
8c3a5a44
KZ
1666static int ui_table_goto(struct cfdisk *cf, int where)
1667{
1668 size_t old;
1669 size_t nparts = fdisk_table_get_nents(cf->table);
6dbe3af9 1670
14620822 1671 DBG(UI, ul_debug("goto table %d", where));
6dbe3af9 1672
8c3a5a44
KZ
1673 if (where < 0)
1674 where = 0;
1675 else if ((size_t) where > nparts - 1)
1676 where = nparts - 1;
6dbe3af9 1677
8c3a5a44
KZ
1678 if ((size_t) where == cf->lines_idx)
1679 return 0;
6dbe3af9 1680
8c3a5a44
KZ
1681 old = cf->lines_idx;
1682 cf->lines_idx = where;
6dbe3af9 1683
a6f69126
KZ
1684 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1685 ui_draw_table(cf);
1686 else {
1687 ui_draw_partition(cf, old); /* cleanup old */
1688 ui_draw_partition(cf, where); /* draw new */
1689 }
1af8003b 1690 ui_clean_info();
8c3a5a44 1691 ui_draw_menu(cf);
3542fc51 1692 ui_draw_extra(cf);
8c3a5a44 1693 refresh();
dda7fe12 1694
8c3a5a44 1695 return 0;
6dbe3af9
KZ
1696}
1697
8c3a5a44
KZ
1698static int ui_refresh(struct cfdisk *cf)
1699{
57c83d63 1700 struct fdisk_label *lb;
8c3a5a44 1701 char *id = NULL;
57c83d63 1702 uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
ec8a728a 1703 char *strsz;
8c3a5a44 1704
83fa0f80 1705 if (!ui_enabled)
8c3a5a44
KZ
1706 return -EINVAL;
1707
39f37044
KZ
1708 strsz = size_to_human_string(SIZE_DECIMAL_2DIGITS
1709 | SIZE_SUFFIX_SPACE
ec8a728a
KZ
1710 | SIZE_SUFFIX_3LETTER, bytes);
1711
57c83d63
KZ
1712 lb = fdisk_get_label(cf->cxt, NULL);
1713 assert(lb);
1714
a2d0dbb4
KZ
1715 clear();
1716
8c3a5a44
KZ
1717 /* header */
1718 attron(A_BOLD);
57c83d63 1719 ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
8c3a5a44 1720 attroff(A_BOLD);
7085f1e4 1721 ui_center(1, _("Size: %s, %"PRIu64" bytes, %ju sectors"),
57c83d63 1722 strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
8c3a5a44 1723 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1af8003b 1724 ui_center(2, _("Label: %s, identifier: %s"),
57c83d63 1725 fdisk_label_get_name(lb), id);
7eda085c 1726 else
57c83d63 1727 ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
8c3a5a44 1728 free(strsz);
ec8f7121 1729 free(id);
6dbe3af9 1730
8c3a5a44
KZ
1731 ui_draw_table(cf);
1732 ui_draw_menu(cf);
6dbe3af9 1733 refresh();
8c3a5a44 1734 return 0;
6dbe3af9
KZ
1735}
1736
ffab025d 1737static ssize_t ui_get_string(const char *prompt,
b1f58330
KZ
1738 const char *hint, char *buf, size_t len)
1739{
b1f58330 1740 int ln = MENU_START_LINE, cl = 1;
1b6d02fe
KZ
1741 ssize_t rc = -1;
1742 struct mbs_editor *edit;
1743
1744 DBG(UI, ul_debug("ui get string"));
b1f58330 1745
b1f58330
KZ
1746 assert(buf);
1747 assert(len);
1748
1749 move(ln, 0);
1750 clrtoeol();
1751
a89eafed
KZ
1752 move(ln + 1, 0);
1753 clrtoeol();
1754
b1f58330 1755 if (prompt) {
f2cfb235 1756 mvaddstr(ln, cl, prompt);
b1f58330
KZ
1757 cl += mbs_safe_width(prompt);
1758 }
1759
1b6d02fe
KZ
1760 edit = mbs_new_edit(buf, len, ui_cols - cl);
1761 if (!edit)
1762 goto done;
1763
1764 mbs_edit_goto(edit, MBS_EDIT_END);
b1f58330
KZ
1765
1766 if (hint)
1767 ui_hint(hint);
1768 else
1769 ui_clean_hint();
1770
b1f58330 1771 curs_set(1);
b1f58330 1772
502c5186 1773 while (!sig_die) {
1b6d02fe
KZ
1774 wint_t c; /* we have fallback in widechar.h */
1775
1776 move(ln, cl);
1777 clrtoeol();
1778 mvaddstr(ln, cl, edit->buf);
1779 move(ln, cl + edit->cursor_cells);
1780 refresh();
1781
b1f58330
KZ
1782#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1783 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1784 if (get_wch(&c) == ERR) {
1785#else
c71f5a56 1786 if ((c = getch()) == (wint_t) ERR) {
b1f58330 1787#endif
502c5186
KZ
1788 if (sig_die)
1789 break;
c431d901 1790 if (sig_resize) {
7556c944
KZ
1791 resize();
1792 continue;
1793 }
b1f58330
KZ
1794 if (!isatty(STDIN_FILENO))
1795 exit(2);
1796 else
1797 goto done;
1798 }
1b6d02fe
KZ
1799
1800 DBG(UI, ul_debug("ui get string: key=%lc", c));
1801
b1f58330
KZ
1802 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1803 break;
1804
1b6d02fe
KZ
1805 rc = 1;
1806
b1f58330
KZ
1807 switch (c) {
1808 case KEY_ESC:
1809 rc = -CFDISK_ERR_ESC;
1810 goto done;
1b6d02fe
KZ
1811 case KEY_LEFT:
1812 rc = mbs_edit_goto(edit, MBS_EDIT_LEFT);
1813 break;
635a8151 1814 case KEY_RIGHT:
1b6d02fe
KZ
1815 rc = mbs_edit_goto(edit, MBS_EDIT_RIGHT);
1816 break;
635a8151 1817 case KEY_END:
1b6d02fe
KZ
1818 rc = mbs_edit_goto(edit, MBS_EDIT_END);
1819 break;
635a8151 1820 case KEY_HOME:
1b6d02fe
KZ
1821 rc = mbs_edit_goto(edit, MBS_EDIT_HOME);
1822 break;
635a8151
KZ
1823 case KEY_UP:
1824 case KEY_DOWN:
635a8151 1825 break;
1b6d02fe
KZ
1826 case KEY_DC:
1827 rc = mbs_edit_delete(edit);
1828 break;
b1f58330 1829 case '\b':
6b1e439a 1830 case KEY_DELETE:
b1f58330 1831 case KEY_BACKSPACE:
1b6d02fe 1832 rc = mbs_edit_backspace(edit);
b1f58330
KZ
1833 break;
1834 default:
1b6d02fe
KZ
1835 rc = mbs_edit_insert(edit, c);
1836 break;
b1f58330 1837 }
1b6d02fe
KZ
1838 if (rc == 1)
1839 beep();
b1f58330
KZ
1840 }
1841
502c5186
KZ
1842 if (sig_die)
1843 die_on_signal();
1844
1b6d02fe 1845 rc = strlen(edit->buf); /* success */
b1f58330
KZ
1846done:
1847 move(ln, 0);
1848 clrtoeol();
1849 curs_set(0);
1850 refresh();
1b6d02fe 1851 mbs_free_edit(edit);
b1f58330
KZ
1852
1853 return rc;
1854}
1855
a351f2e5
KZ
1856static int ui_get_size(struct cfdisk *cf, /* context */
1857 const char *prompt, /* UI dialog string */
7085f1e4
KZ
1858 uint64_t *res, /* result in bytes */
1859 uint64_t low, /* minimal size */
1860 uint64_t up, /* maximal size */
a351f2e5 1861 int *expsize) /* explicitly specified size */
b1f58330
KZ
1862{
1863 char buf[128];
7085f1e4 1864 uint64_t user = 0;
b1f58330
KZ
1865 ssize_t rc;
1866 char *dflt = size_to_human_string(0, *res);
1867
33342316 1868 DBG(UI, ul_debug("get_size (default=%"PRIu64")", *res));
b1f58330
KZ
1869
1870 ui_clean_info();
1871
29e9b787
KZ
1872 snprintf(buf, sizeof(buf), "%s", dflt);
1873
b1f58330 1874 do {
91ba41ca 1875 int pwr = 0, insec = 0;
b1f58330 1876
ffab025d 1877 rc = ui_get_string(prompt,
a4fbbbd4
BS
1878 _("May be followed by M for MiB, G for GiB, "
1879 "T for TiB, or S for sectors."),
b1f58330 1880 buf, sizeof(buf));
29e9b787
KZ
1881 ui_clean_warn();
1882
b1f58330
KZ
1883 if (rc == 0) {
1884 ui_warnx(_("Please, specify size."));
1885 continue; /* nothing specified */
042f62df 1886 } if (rc == -CFDISK_ERR_ESC)
b1f58330
KZ
1887 break; /* cancel dialog */
1888
91ba41ca
KZ
1889 if (strcmp(buf, dflt) == 0)
1890 user = *res, rc = 0; /* no change, use default */
1891 else {
1892 size_t len = strlen(buf);
bfdb3244 1893 if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
91ba41ca
KZ
1894 insec = 1;
1895 buf[len - 1] = '\0';
1896 }
33342316 1897 rc = parse_size(buf, (uintmax_t *)&user, &pwr); /* parse */
91ba41ca
KZ
1898 }
1899
b1f58330 1900 if (rc == 0) {
7085f1e4 1901 DBG(UI, ul_debug("get_size user=%"PRIu64", power=%d, in-sectors=%s",
91ba41ca
KZ
1902 user, pwr, insec ? "yes" : "no"));
1903 if (insec)
57c83d63 1904 user *= fdisk_get_sector_size(cf->cxt);
b1f58330 1905 if (user < low) {
7085f1e4 1906 ui_warnx(_("Minimum size is %"PRIu64" bytes."), low);
b1f58330
KZ
1907 rc = -ERANGE;
1908 }
1909 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1910 /* ignore when the user specified size overflow
1911 * with in range specified by suffix (e.g. MiB) */
1912 user = up;
1913
1914 if (user > up) {
7085f1e4 1915 ui_warnx(_("Maximum size is %"PRIu64" bytes."), up);
b1f58330
KZ
1916 rc = -ERANGE;
1917 }
9b0b9fb1
KZ
1918 if (rc == 0 && insec && expsize)
1919 *expsize = 1;
1920
2cec7949
KZ
1921 } else
1922 ui_warnx(_("Failed to parse size."));
b1f58330
KZ
1923 } while (rc != 0);
1924
1925 if (rc == 0)
1926 *res = user;
1927 free(dflt);
1928
7085f1e4 1929 DBG(UI, ul_debug("get_size (result=%"PRIu64", rc=%zd)", *res, rc));
b1f58330
KZ
1930 return rc;
1931}
1932
6d6d9c1a
KZ
1933static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1934 struct fdisk_parttype *cur)
6fed9601 1935{
3b411726 1936 struct cfdisk_menuitem *d, *cm;
6d6d9c1a 1937 size_t i = 0, nitems, idx = 0;
a745611d 1938 struct fdisk_parttype *t = NULL;
5ab37600 1939 struct fdisk_label *lb;
a745611d 1940 int codetypes = 0;
6fed9601 1941
14620822 1942 DBG(UI, ul_debug("asking for parttype."));
6fed9601 1943
5ab37600
KZ
1944 lb = fdisk_get_label(cf->cxt, NULL);
1945
6fed9601
KZ
1946 /* create cfdisk menu according to label types, note that the
1947 * last cm[] item has to be empty -- so nitems + 1 */
a745611d
KZ
1948 nitems = fdisk_label_get_nparttypes(lb);
1949 if (!nitems)
6d6d9c1a 1950 return NULL;
5ab37600 1951
3b411726 1952 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
6fed9601
KZ
1953 if (!cm)
1954 return NULL;
1955
a745611d 1956 codetypes = fdisk_label_has_code_parttypes(lb);
6d6d9c1a 1957
6fed9601 1958 for (i = 0; i < nitems; i++) {
a745611d 1959 const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
6d6d9c1a 1960 char *name;
6fed9601 1961
a745611d
KZ
1962 cm[i].userdata = (void *) x;
1963 if (codetypes)
1964 xasprintf(&name, "%2x %s",
1965 fdisk_parttype_get_code(x),
1966 _(fdisk_parttype_get_name(x)));
6d6d9c1a 1967 else {
a745611d
KZ
1968 name = (char *) _(fdisk_parttype_get_name(x));
1969 cm[i].desc = fdisk_parttype_get_string(x);
6d6d9c1a
KZ
1970 }
1971 cm[i].name = name;
1972 if (x == cur)
1973 idx = i;
6fed9601
KZ
1974 }
1975
1976 /* make the new menu active */
1977 menu_push(cf, cm);
1978 cf->menu->vertical = 1;
6d6d9c1a 1979 cf->menu->idx = idx;
6fed9601
KZ
1980 menu_set_title(cf->menu, _("Select partition type"));
1981 ui_draw_menu(cf);
1982 refresh();
1983
502c5186 1984 while (!sig_die) {
6fed9601 1985 int key = getch();
f019ce1f 1986
502c5186
KZ
1987 if (sig_die)
1988 break;
c431d901 1989 if (sig_resize)
7556c944 1990 ui_menu_resize(cf);
6fed9601
KZ
1991 if (ui_menu_move(cf, key) == 0)
1992 continue;
f019ce1f 1993
6fed9601
KZ
1994 switch (key) {
1995 case KEY_ENTER:
1996 case '\n':
1997 case '\r':
1998 d = menu_get_menuitem(cf, cf->menu->idx);
1999 if (d)
2000 t = (struct fdisk_parttype *) d->userdata;
2001 goto done;
ad5d45c0 2002 case KEY_ESC:
6fed9601
KZ
2003 case 'q':
2004 case 'Q':
2005 goto done;
2006 }
502c5186 2007 }
6fed9601 2008
502c5186
KZ
2009 if (sig_die)
2010 die_on_signal();
6fed9601
KZ
2011done:
2012 menu_pop(cf);
a745611d 2013 if (codetypes) {
6d6d9c1a
KZ
2014 for (i = 0; i < nitems; i++)
2015 free((char *) cm[i].name);
2016 }
6fed9601 2017 free(cm);
57c83d63
KZ
2018 DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
2019 fdisk_parttype_get_name(t) : NULL));
6fed9601
KZ
2020 return t;
2021}
2022
a89eafed
KZ
2023static int ui_script_read(struct cfdisk *cf)
2024{
2025 struct fdisk_script *sc = NULL;
2026 char buf[PATH_MAX] = { 0 };
2027 int rc;
2028
941409c0 2029 erase();
ffab025d 2030 rc = ui_get_string( _("Enter script file name: "),
a89eafed
KZ
2031 _("The script file will be applied to in-memory partition table."),
2032 buf, sizeof(buf));
2033 if (rc <= 0)
2034 return rc;
2035
2036 rc = -1;
2037 errno = 0;
2038 sc = fdisk_new_script_from_file(cf->cxt, buf);
2039 if (!sc && errno)
54fefa07 2040 ui_warn(_("Cannot open %s"), buf);
a89eafed
KZ
2041 else if (!sc)
2042 ui_warnx(_("Failed to parse script file %s"), buf);
2043 else if (fdisk_apply_script(cf->cxt, sc) != 0)
2044 ui_warnx(_("Failed to apply script %s"), buf);
2045 else
2046 rc = 0;
2047
941409c0 2048 ui_clean_hint();
a89eafed
KZ
2049 fdisk_unref_script(sc);
2050 return rc;
2051}
2052
2053static int ui_script_write(struct cfdisk *cf)
2054{
2055 struct fdisk_script *sc = NULL;
2056 char buf[PATH_MAX] = { 0 };
2057 FILE *f = NULL;
2058 int rc;
2059
ffab025d 2060 rc = ui_get_string( _("Enter script file name: "),
a89eafed
KZ
2061 _("The current in-memory partition table will be dumped to the file."),
2062 buf, sizeof(buf));
2063 if (rc <= 0)
2064 return rc;
2065
2066 rc = 0;
2067 sc = fdisk_new_script(cf->cxt);
2068 if (!sc) {
2069 ui_warn(_("Failed to allocate script handler"));
2070 goto done;
2071 }
2072
2073 rc = fdisk_script_read_context(sc, NULL);
2074 if (rc) {
2075 ui_warnx(_("Failed to read disk layout into script."));
2076 goto done;
2077 }
2078
8c9615a9 2079 DBG(UI, ul_debug("writing dump into: '%s'", buf));
a89eafed
KZ
2080 f = fopen(buf, "w");
2081 if (!f) {
54fefa07 2082 ui_warn(_("Cannot open %s"), buf);
8c9615a9 2083 rc = -errno;
a89eafed
KZ
2084 goto done;
2085 }
2086
2087 rc = fdisk_script_write_file(sc, f);
8c9615a9
KZ
2088 if (!rc)
2089 ui_info(_("Disk layout successfully dumped."));
2090done:
a89eafed
KZ
2091 if (rc)
2092 ui_warn(_("Failed to write script %s"), buf);
a89eafed
KZ
2093 if (f)
2094 fclose(f);
2095 fdisk_unref_script(sc);
2096 return rc;
2097}
2098
7aa0d529
KZ
2099/* prints menu with libfdisk labels and waits for users response */
2100static int ui_create_label(struct cfdisk *cf)
2101{
3b411726 2102 struct cfdisk_menuitem *d, *cm;
941409c0 2103 int rc = 1, refresh_menu = 1;
7aa0d529
KZ
2104 size_t i = 0, nitems;
2105 struct fdisk_label *lb = NULL;
2106
2107 assert(cf);
2108
14620822 2109 DBG(UI, ul_debug("asking for new disklabe."));
7aa0d529
KZ
2110
2111 /* create cfdisk menu according to libfdisk labels, note that the
2112 * last cm[] item has to be empty -- so nitems + 1 */
6a632136 2113 nitems = fdisk_get_nlabels(cf->cxt);
3b411726 2114 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
7aa0d529 2115
6a632136 2116 while (fdisk_next_label(cf->cxt, &lb) == 0) {
57c83d63 2117 if (fdisk_label_is_disabled(lb) ||
8eccde20 2118 fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
dd626abd 2119 continue;
57c83d63 2120 cm[i++].name = fdisk_label_get_name(lb);
7aa0d529
KZ
2121 }
2122
2123 erase();
7aa0d529
KZ
2124
2125 /* make the new menu active */
63128bbf 2126 menu_push(cf, cm);
7aa0d529
KZ
2127 cf->menu->vertical = 1;
2128 menu_set_title(cf->menu, _("Select label type"));
941409c0
KZ
2129
2130 if (!cf->zero_start)
2131 ui_info(_("Device does not contain a recognized partition table."));
2132
7aa0d529 2133
502c5186 2134 while (!sig_die) {
c308e205
SK
2135 int key;
2136
941409c0
KZ
2137 if (refresh_menu) {
2138 ui_draw_menu(cf);
2139 ui_hint(_("Select a type to create a new label or press 'L' to load script file."));
2140 refresh();
2141 refresh_menu = 0;
2142 }
2143
c308e205 2144 key = getch();
7556c944 2145
502c5186
KZ
2146 if (sig_die)
2147 break;
c431d901 2148 if (sig_resize)
7556c944 2149 ui_menu_resize(cf);
7aa0d529
KZ
2150 if (ui_menu_move(cf, key) == 0)
2151 continue;
2152 switch (key) {
2153 case KEY_ENTER:
2154 case '\n':
2155 case '\r':
6fed9601 2156 d = menu_get_menuitem(cf, cf->menu->idx);
7aa0d529
KZ
2157 if (d)
2158 rc = fdisk_create_disklabel(cf->cxt, d->name);
2159 goto done;
ad5d45c0 2160 case KEY_ESC:
7aa0d529
KZ
2161 case 'q':
2162 case 'Q':
2163 goto done;
f16c37f4 2164 case 'l':
a89eafed 2165 case 'L':
a89eafed
KZ
2166 rc = ui_script_read(cf);
2167 if (rc == 0)
2168 goto done;
941409c0 2169 refresh_menu = 1;
a89eafed 2170 break;
7aa0d529 2171 }
502c5186 2172 }
7aa0d529 2173
502c5186
KZ
2174 if (sig_die)
2175 die_on_signal();
7aa0d529
KZ
2176done:
2177 menu_pop(cf);
2178 free(cm);
14620822 2179 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
7aa0d529
KZ
2180 return rc;
2181}
2182
a89eafed 2183
314a0dd8
KZ
2184static int ui_help(void)
2185{
2186 size_t i;
314a0dd8 2187 static const char *help[] = {
e993b9c8
BS
2188 N_("This is cfdisk, a curses-based disk partitioning program."),
2189 N_("It lets you create, delete, and modify partitions on a block device."),
fd4484a7 2190 " ",
314a0dd8
KZ
2191 N_("Command Meaning"),
2192 N_("------- -------"),
2193 N_(" b Toggle bootable flag of the current partition"),
2194 N_(" d Delete the current partition"),
2195 N_(" h Print this screen"),
2196 N_(" n Create new partition from free space"),
2197 N_(" q Quit program without writing partition table"),
33f7c502 2198 N_(" s Fix partitions order (only when in disarray)"),
e993b9c8 2199 N_(" t Change the partition type"),
a89eafed 2200 N_(" u Dump disk layout to sfdisk compatible script file"),
e993b9c8
BS
2201 N_(" W Write partition table to disk (you must enter uppercase W);"),
2202 N_(" since this might destroy data on the disk, you must either"),
2203 N_(" confirm or deny the write by entering 'yes' or 'no'"),
dda7fe12 2204 N_(" x Display/hide extra information about a partition"),
314a0dd8
KZ
2205 N_("Up Arrow Move cursor to the previous partition"),
2206 N_("Down Arrow Move cursor to the next partition"),
2207 N_("Left Arrow Move cursor to the previous menu item"),
2208 N_("Right Arrow Move cursor to the next menu item"),
fd4484a7 2209 " ",
314a0dd8 2210 N_("Note: All of the commands can be entered with either upper or lower"),
e993b9c8 2211 N_("case letters (except for Write)."),
fd4484a7 2212 " ",
e7e76976
KZ
2213 N_("Use lsblk(8) or partx(8) to see more details about the device."),
2214 " ",
2215 " ",
2216 "Copyright (C) 2014-2017 Karel Zak <kzak@redhat.com>"
314a0dd8
KZ
2217 };
2218
2219 erase();
314a0dd8
KZ
2220 for (i = 0; i < ARRAY_SIZE(help); i++)
2221 mvaddstr(i, 1, _(help[i]));
2222
2223 ui_info(_("Press a key to continue."));
7556c944 2224
314a0dd8 2225 getch();
502c5186
KZ
2226
2227 if (sig_die)
2228 die_on_signal();
314a0dd8
KZ
2229 return 0;
2230}
2231
63128bbf
KZ
2232/* TODO: use @sz, now 128bytes */
2233static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
2234 size_t sz __attribute__((__unused__)))
2235{
2236 struct fdisk_partition *pa = get_current_partition(cf);
2237 size_t i = 0;
2238
2239 if (!pa)
2240 return 0;
2241 if (fdisk_partition_is_freespace(pa)) {
2242 ignore[i++] = 'd'; /* delete */
2243 ignore[i++] = 't'; /* set type */
2244 ignore[i++] = 'b'; /* set bootable */
a351f2e5 2245 ignore[i++] = 'r'; /* resize */
b5ef65a9 2246 cf->menu->prefkey = 'n';
63128bbf 2247 } else {
b5ef65a9 2248 cf->menu->prefkey = 'q';
63128bbf 2249 ignore[i++] = 'n';
aa36c2cf
KZ
2250 if (!fdisk_is_label(cf->cxt, DOS) &&
2251 !fdisk_is_label(cf->cxt, SGI))
63128bbf
KZ
2252 ignore[i++] = 'b';
2253 }
e146ae4e 2254
d5314bf5
KZ
2255 if (!cf->wrong_order)
2256 ignore[i++] = 's';
f9fb8290 2257
6a632136 2258 if (fdisk_is_readonly(cf->cxt))
e146ae4e 2259 ignore[i++] = 'W';
dda7fe12 2260
63128bbf
KZ
2261 return i;
2262}
2263
2264
ed8a13e6
KZ
2265/* returns: error: < 0, success: 0, quit: 1 */
2266static int main_menu_action(struct cfdisk *cf, int key)
2267{
2268 size_t n;
d5314bf5 2269 int ref = 0, rc, org_order = cf->wrong_order;
ed8a13e6
KZ
2270 const char *info = NULL, *warn = NULL;
2271 struct fdisk_partition *pa;
2272
2273 assert(cf);
2274 assert(cf->cxt);
2275 assert(cf->menu);
2276
2277 if (key == 0) {
3b411726 2278 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
ed8a13e6
KZ
2279 if (!d)
2280 return 0;
2281 key = d->key;
2282
2283 } else if (key != 'w' && key != 'W')
2284 key = tolower(key); /* case insensitive except 'W'rite */
2285
14620822 2286 DBG(MENU, ul_debug("main menu action: key=%c", key));
ed8a13e6
KZ
2287
2288 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
14620822 2289 DBG(MENU, ul_debug(" ignore '%c'", key));
ed8a13e6
KZ
2290 return 0;
2291 }
2292
2293 pa = get_current_partition(cf);
ec8a728a
KZ
2294 if (!pa)
2295 return -EINVAL;
ed8a13e6
KZ
2296 n = fdisk_partition_get_partno(pa);
2297
14620822 2298 DBG(MENU, ul_debug("menu action on %p", pa));
7b1ffbc4
KZ
2299 ui_clean_hint();
2300 ui_clean_info();
ed8a13e6
KZ
2301
2302 switch (key) {
2303 case 'b': /* Bootable flag */
2304 {
aa36c2cf
KZ
2305 int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
2306 fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
ed8a13e6 2307
a1ef792f 2308 if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
ed8a13e6
KZ
2309 warn = _("Could not toggle the flag.");
2310 else if (fl)
2311 ref = 1;
2312 break;
2313 }
f1512be8 2314#ifdef KEY_DC
f019ce1f 2315 case KEY_DC:
f1512be8 2316#endif
ed8a13e6
KZ
2317 case 'd': /* Delete */
2318 if (fdisk_delete_partition(cf->cxt, n) != 0)
2319 warn = _("Could not delete partition %zu.");
2320 else
2321 info = _("Partition %zu has been deleted.");
2322 ref = 1;
2323 break;
d4640320 2324 case 'h': /* Help */
2a0ffa31 2325 case '?':
314a0dd8
KZ
2326 ui_help();
2327 ref = 1;
2328 break;
ed8a13e6
KZ
2329 case 'n': /* New */
2330 {
a351f2e5 2331 uint64_t start, size, dflt_size, secs, max_size;
ed8a13e6 2332 struct fdisk_partition *npa; /* the new partition */
9b0b9fb1 2333 int expsize = 0; /* size specified explicitly in sectors */
ed8a13e6 2334
ec8a728a 2335 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
ed8a13e6 2336 return -EINVAL;
75090201 2337
ed8a13e6
KZ
2338 /* free space range */
2339 start = fdisk_partition_get_start(pa);
a351f2e5 2340 size = max_size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
ed8a13e6 2341
1bb387bd
KZ
2342 if (ui_get_size(cf, _("Partition size: "), &size,
2343 fdisk_get_sector_size(cf->cxt),
a351f2e5 2344 max_size, &expsize) == -CFDISK_ERR_ESC)
ed8a13e6
KZ
2345 break;
2346
75090201 2347 secs = size / fdisk_get_sector_size(cf->cxt);
75090201
KZ
2348
2349 npa = fdisk_new_partition();
2350 if (!npa)
2351 return -ENOMEM;
2352
ed8a13e6
KZ
2353 if (dflt_size == size) /* default is to fillin all free space */
2354 fdisk_partition_end_follow_default(npa, 1);
75090201
KZ
2355 else
2356 fdisk_partition_set_size(npa, secs);
ed8a13e6 2357
9b0b9fb1
KZ
2358 if (expsize)
2359 fdisk_partition_size_explicit(pa, 1);
2360
ed8a13e6 2361 fdisk_partition_set_start(npa, start);
9b0b9fb1 2362 fdisk_partition_partno_follow_default(npa, 1);
ed8a13e6 2363 /* add to disk label -- libfdisk will ask for missing details */
c3bc7483 2364 rc = fdisk_add_partition(cf->cxt, npa, NULL);
ed8a13e6
KZ
2365 fdisk_unref_partition(npa);
2366 if (rc == 0)
2367 ref = 1;
2368 break;
2369 }
2370 case 'q': /* Quit */
2371 return 1;
2372 case 't': /* Type */
6fed9601 2373 {
6fed9601
KZ
2374 struct fdisk_parttype *t;
2375
ec8a728a 2376 if (fdisk_partition_is_freespace(pa))
6fed9601
KZ
2377 return -EINVAL;
2378 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
6d6d9c1a 2379 t = ui_get_parttype(cf, t);
6fed9601
KZ
2380 ref = 1;
2381
2382 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
09af3db4 2383 info = _("Changed type of partition %zu.");
6fed9601 2384 else
09af3db4 2385 info = _("The type of partition %zu is unchanged.");
ed8a13e6 2386 break;
6fed9601 2387 }
a351f2e5
KZ
2388 case 'r': /* resize */
2389 {
2390 struct fdisk_partition *npa, *next;
2391 uint64_t size, max_size, secs;
2392
2393 if (fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
2394 return -EINVAL;
2395
2396 size = fdisk_partition_get_size(pa);
2397
2398 /* is the next freespace? */
2399 next = fdisk_table_get_partition(cf->table, cf->lines_idx + 1);
2400 if (next && fdisk_partition_is_freespace(next))
2401 size += fdisk_partition_get_size(next);
2402
2403 size *= fdisk_get_sector_size(cf->cxt);
2404 max_size = size;
2405
2406 if (ui_get_size(cf, _("New size: "), &size,
2407 fdisk_get_sector_size(cf->cxt),
2408 max_size, NULL) == -CFDISK_ERR_ESC)
2409 break;
2410 secs = size / fdisk_get_sector_size(cf->cxt);
2411 npa = fdisk_new_partition();
2412 if (!npa)
2413 return -ENOMEM;
2414
2415 fdisk_partition_set_size(npa, secs);
2416
2417 rc = fdisk_set_partition(cf->cxt, n, npa);
2418 fdisk_unref_partition(npa);
2419 if (rc == 0) {
2420 ref = 1;
2421 info = _("Partition %zu resized.");
2422 }
2423 break;
2424 }
d4640320 2425 case 's': /* Sort */
d5314bf5
KZ
2426 if (cf->wrong_order) {
2427 fdisk_reorder_partitions(cf->cxt);
2428 ref = 1;
2429 }
2430 break;
d4640320 2431 case 'u': /* dUmp */
8c9615a9 2432 ui_script_write(cf);
a89eafed 2433 break;
ed8a13e6 2434 case 'W': /* Write */
7b1ffbc4
KZ
2435 {
2436 char buf[64] = { 0 };
e146ae4e 2437
6a632136 2438 if (fdisk_is_readonly(cf->cxt)) {
d4640320 2439 warn = _("Device is open in read-only mode.");
e146ae4e
KZ
2440 break;
2441 }
2442
ffab025d 2443 rc = ui_get_string(
7b1ffbc4
KZ
2444 _("Are you sure you want to write the partition "
2445 "table to disk? "),
09af3db4 2446 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
7b1ffbc4
KZ
2447 buf, sizeof(buf));
2448
2449 ref = 1;
59af21c3
KZ
2450 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2451 strcasecmp(buf, _("yes")) != 0)) {
d4640320 2452 info = _("Did not write partition table to disk.");
7b1ffbc4
KZ
2453 break;
2454 }
ed8a13e6
KZ
2455 rc = fdisk_write_disklabel(cf->cxt);
2456 if (rc)
d4640320 2457 warn = _("Failed to write disklabel.");
7b1ffbc4 2458 else {
044ebc0c
KZ
2459 if (cf->device_is_used)
2460 fdisk_reread_changes(cf->cxt, cf->original_layout);
2461 else
2462 fdisk_reread_partition_table(cf->cxt);
7b1ffbc4
KZ
2463 info = _("The partition table has been altered.");
2464 }
bc787727 2465 cf->nwrites++;
7b1ffbc4
KZ
2466 break;
2467 }
2468 default:
ed8a13e6
KZ
2469 break;
2470 }
2471
2472 if (ref) {
2473 lines_refresh(cf);
2474 ui_refresh(cf);
3542fc51 2475 ui_draw_extra(cf);
a89eafed
KZ
2476 } else
2477 ui_draw_menu(cf);
7b1ffbc4
KZ
2478
2479 ui_clean_hint();
75090201 2480
ed8a13e6 2481 if (warn)
7b1ffbc4 2482 ui_warnx(warn, n + 1);
ed8a13e6 2483 else if (info)
7b1ffbc4 2484 ui_info(info, n + 1);
d5314bf5
KZ
2485 else if (key == 'n' && cf->wrong_order && org_order == 0)
2486 ui_info(_("Note that partition table entries are not in disk order now."));
ed8a13e6
KZ
2487
2488 return 0;
2489}
2490
7556c944
KZ
2491static void ui_resize_refresh(struct cfdisk *cf)
2492{
2f4eb047 2493 DBG(UI, ul_debug("ui resize/refresh"));
7556c944
KZ
2494 resize();
2495 menu_refresh_size(cf);
2496 lines_refresh(cf);
2497 ui_refresh(cf);
3542fc51 2498 ui_draw_extra(cf);
7556c944
KZ
2499}
2500
a2d0dbb4
KZ
2501static void toggle_show_extra(struct cfdisk *cf)
2502{
2503 if (cf->show_extra && cf->act_win) {
2504 wclear(cf->act_win);
2505 touchwin(stdscr);
2506 }
2507 cf->show_extra = cf->show_extra ? 0 : 1;
2508
2509 if (cf->show_extra)
2510 ui_draw_extra(cf);
2511 DBG(MENU, ul_debug("extra: %s", cf->show_extra ? "ENABLED" : "DISABLED" ));
2512}
2513
8c3a5a44
KZ
2514static int ui_run(struct cfdisk *cf)
2515{
7aa0d529 2516 int rc = 0;
6dbe3af9 2517
eef63970
KZ
2518 ui_lines = LINES;
2519 ui_cols = COLS;
2520 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
6dbe3af9 2521
97ffbd43 2522 if (fdisk_get_collision(cf->cxt)) {
cb19f547 2523 ui_warnx(_("Device already contains a %s signature; it will be removed by a write command."),
97ffbd43
KZ
2524 fdisk_get_collision(cf->cxt));
2525 fdisk_enable_wipe(cf->cxt, 1);
2526 ui_hint(_("Press a key to continue."));
2527 getch();
2528 }
2529
aa36c2cf 2530 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
7aa0d529
KZ
2531 rc = ui_create_label(cf);
2532 if (rc < 0)
2533 ui_errx(EXIT_FAILURE,
2534 _("failed to create a new disklabel"));
2535 if (rc)
2536 return rc;
2537 }
2538
2539 cols_init(cf);
2540 rc = lines_refresh(cf);
2541 if (rc)
2542 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
6dbe3af9 2543
3b411726 2544 menu_push(cf, main_menuitems);
63128bbf
KZ
2545 cf->menu->ignore_cb = main_menu_ignore_keys;
2546
8c3a5a44
KZ
2547 rc = ui_refresh(cf);
2548 if (rc)
2549 return rc;
6dbe3af9 2550
dda7fe12 2551 cf->show_extra = 1;
3542fc51 2552 ui_draw_extra(cf);
dda7fe12 2553
6a632136 2554 if (fdisk_is_readonly(cf->cxt))
62eea9ce 2555 ui_warnx(_("Device is open in read-only mode."));
f9fb8290 2556 else if (cf->wrong_order)
a2d0dbb4 2557 ui_info(_("Note that partition table entries are not in disk order now."));
e146ae4e 2558
502c5186 2559 while (!sig_die) {
7ee26cbf 2560 int key = getch();
6dbe3af9 2561
7ee26cbf 2562 rc = 0;
502c5186
KZ
2563
2564 if (sig_die)
2565 break;
c431d901 2566 if (sig_resize)
7556c944
KZ
2567 /* Note that ncurses getch() returns ERR when interrupted
2568 * by signal, but SLang does not interrupt at all. */
2569 ui_resize_refresh(cf);
2570 if (key == ERR)
2571 continue;
2f4eb047
KZ
2572 if (key == '\014') { /* ^L refresh */
2573 ui_resize_refresh(cf);
2574 continue;
2575 }
ac27ea5c
KZ
2576 if (ui_menu_move(cf, key) == 0)
2577 continue;
2578
2f4eb047 2579 DBG(UI, ul_debug("main action key >%1$c< [\\0%1$o].", key));
f019ce1f 2580
8c3a5a44
KZ
2581 switch (key) {
2582 case KEY_DOWN:
2583 case '\016': /* ^N */
2584 case 'j': /* Vi-like alternative */
2585 ui_table_goto(cf, cf->lines_idx + 1);
2586 break;
2587 case KEY_UP:
2588 case '\020': /* ^P */
2589 case 'k': /* Vi-like alternative */
f019ce1f 2590 ui_table_goto(cf, (int) cf->lines_idx - 1);
8c3a5a44 2591 break;
f019ce1f
KZ
2592 case KEY_PPAGE:
2593 if (cf->page_sz) {
2594 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2595 break;
2596 }
08099e41 2597 /* fallthrough */
8c3a5a44
KZ
2598 case KEY_HOME:
2599 ui_table_goto(cf, 0);
2600 break;
f019ce1f
KZ
2601 case KEY_NPAGE:
2602 if (cf->page_sz) {
2603 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2604 break;
2605 }
08099e41 2606 /* fallthrough */
8c3a5a44 2607 case KEY_END:
f019ce1f 2608 ui_table_goto(cf, (int) cf->nlines - 1);
8c3a5a44 2609 break;
8c3a5a44
KZ
2610 case KEY_ENTER:
2611 case '\n':
2612 case '\r':
ed8a13e6 2613 rc = main_menu_action(cf, 0);
8c3a5a44 2614 break;
80351b1f 2615 case 'X':
3542fc51 2616 case 'x': /* Extra */
a2d0dbb4 2617 toggle_show_extra(cf);
3542fc51 2618 break;
8c3a5a44 2619 default:
ed8a13e6 2620 rc = main_menu_action(cf, key);
8460875d 2621 if (rc < 0)
8c3a5a44
KZ
2622 beep();
2623 break;
2624 }
8460875d
KZ
2625
2626 if (rc == 1)
2627 break; /* quit */
502c5186 2628 }
6dbe3af9 2629
8c3a5a44 2630 menu_pop(cf);
6dbe3af9 2631
14620822 2632 DBG(UI, ul_debug("end"));
8c3a5a44
KZ
2633 return 0;
2634}
6dbe3af9 2635
86be6a32 2636static void __attribute__((__noreturn__)) usage(void)
04915c3f 2637{
86be6a32 2638 FILE *out = stdout;
04915c3f 2639 fputs(USAGE_HEADER, out);
04915c3f
KZ
2640 fprintf(out,
2641 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2642
451dbcfa
BS
2643 fputs(USAGE_SEPARATOR, out);
2644 fputs(_("Display or manipulate a disk partition table.\n"), out);
2645
04915c3f 2646 fputs(USAGE_OPTIONS, out);
a7466bdc
SK
2647 fprintf(out,
2648 _(" -L, --color[=<when>] colorize output (%s, %s or %s)\n"), "auto", "always", "never");
5d51dc2a
KZ
2649 fprintf(out,
2650 " %s\n", USAGE_COLORS_DEFAULT);
2651 fputs(_(" -z, --zero start with zeroed partition table\n"), out);
ec8f7121
KZ
2652 fprintf(out,
2653 _(" --lock[=<mode>] use exclusive device lock (%s, %s or %s)\n"), "yes", "no", "nonblock");
04915c3f
KZ
2654
2655 fputs(USAGE_SEPARATOR, out);
f45f3ec3 2656 printf(USAGE_HELP_OPTIONS(26));
04915c3f 2657
f45f3ec3 2658 printf(USAGE_MAN_TAIL("cfdisk(8)"));
86be6a32 2659 exit(EXIT_SUCCESS);
04915c3f
KZ
2660}
2661
8c3a5a44
KZ
2662int main(int argc, char *argv[])
2663{
ec8f7121 2664 const char *diskpath = NULL, *lockmode = NULL;
ad9fe47e 2665 int rc, c, colormode = UL_COLORMODE_UNDEF;
8c3a5a44
KZ
2666 struct cfdisk _cf = { .lines_idx = 0 },
2667 *cf = &_cf;
ec8f7121
KZ
2668 enum {
2669 OPT_LOCK = CHAR_MAX + 1
2670 };
04915c3f
KZ
2671 static const struct option longopts[] = {
2672 { "color", optional_argument, NULL, 'L' },
ec8f7121 2673 { "lock", optional_argument, NULL, OPT_LOCK },
04915c3f
KZ
2674 { "help", no_argument, NULL, 'h' },
2675 { "version", no_argument, NULL, 'V' },
d121efdb 2676 { "zero", no_argument, NULL, 'z' },
87918040 2677 { NULL, 0, NULL, 0 },
04915c3f
KZ
2678 };
2679
8c3a5a44
KZ
2680 setlocale(LC_ALL, "");
2681 bindtextdomain(PACKAGE, LOCALEDIR);
2682 textdomain(PACKAGE);
2c308875 2683 close_stdout_atexit();
6dbe3af9 2684
d121efdb 2685 while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
04915c3f
KZ
2686 switch(c) {
2687 case 'h':
86be6a32 2688 usage();
04915c3f
KZ
2689 break;
2690 case 'L':
2691 colormode = UL_COLORMODE_AUTO;
2692 if (optarg)
2693 colormode = colormode_or_err(optarg,
2694 _("unsupported color mode"));
2695 break;
2696 case 'V':
2c308875 2697 print_version(EXIT_SUCCESS);
d121efdb
KZ
2698 case 'z':
2699 cf->zero_start = 1;
2700 break;
ec8f7121
KZ
2701 case OPT_LOCK:
2702 lockmode = "1";
2703 if (optarg) {
2704 if (*optarg == '=')
2705 optarg++;
2706 lockmode = optarg;
2707 }
2708 break;
677ec86c
KZ
2709 default:
2710 errtryhelp(EXIT_FAILURE);
04915c3f
KZ
2711 }
2712 }
2713
210bb492 2714 colors_init(colormode, "cfdisk");
04915c3f 2715
8c3a5a44 2716 fdisk_init_debug(0);
710ed55d 2717 scols_init_debug(0);
14620822 2718 cfdisk_init_debug();
8c3a5a44
KZ
2719 cf->cxt = fdisk_new_context();
2720 if (!cf->cxt)
2721 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
6dbe3af9 2722
6a632136 2723 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
6dbe3af9 2724
6e51ab0c
KZ
2725 if (optind == argc) {
2726 size_t i;
2727
2728 for (i = 0; i < ARRAY_SIZE(default_disks); i++) {
2729 if (access(default_disks[i], F_OK) == 0) {
2730 diskpath = default_disks[i];
2731 break;
2732 }
2733 }
2734 if (!diskpath)
2735 diskpath = default_disks[0]; /* default, used for "cannot open" */
2736 } else
a4f0a42d 2737 diskpath = argv[optind];
6dbe3af9 2738
6a632136 2739 rc = fdisk_assign_device(cf->cxt, diskpath, 0);
e146ae4e 2740 if (rc == -EACCES)
6a632136 2741 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
e146ae4e 2742 if (rc != 0)
6e51ab0c 2743 err(EXIT_FAILURE, _("cannot open %s"), diskpath);
6dbe3af9 2744
044ebc0c 2745 if (!fdisk_is_readonly(cf->cxt)) {
ec8f7121
KZ
2746 if (blkdev_lock(fdisk_get_devfd(cf->cxt), diskpath, lockmode) != 0)
2747 return EXIT_FAILURE;
2748
044ebc0c
KZ
2749 cf->device_is_used = fdisk_device_is_used(cf->cxt);
2750 fdisk_get_partitions(cf->cxt, &cf->original_layout);
2751 }
2752
8c3a5a44
KZ
2753 /* Don't use err(), warn() from this point */
2754 ui_init(cf);
2755 ui_run(cf);
7aa0d529 2756 ui_end();
6dbe3af9 2757
dda7fe12 2758 cfdisk_free_lines(cf);
8c3a5a44 2759 free(cf->linesbuf);
41994e00 2760 free(cf->fields);
ed8a13e6 2761
dda7fe12
OO
2762 fdisk_unref_table(cf->table);
2763#ifdef HAVE_LIBMOUNT
2764 mnt_unref_table(cf->fstab);
2765 mnt_unref_table(cf->mtab);
2766 mnt_unref_cache(cf->mntcache);
2767#endif
6a632136 2768 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
c7119037 2769 fdisk_unref_context(cf->cxt);
14620822 2770 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
ed8a13e6 2771 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6dbe3af9 2772}