]> git.ipfire.org Git - thirdparty/util-linux.git/blame - disk-utils/cfdisk.c
cfdisk: improve wording and consistency of the size warnings
[thirdparty/util-linux.git] / disk-utils / cfdisk.c
CommitLineData
2ddfd9e4
BS
1/*
2 * cfdisk.c - Display or manipulate a disk partition table.
3 *
4 * Copyright (C) 2014 Karel Zak <kzak@redhat.com>
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
KZ
25
26#ifdef HAVE_SLANG_H
3b411726 27# include <slang.h>
541e6934 28#elif defined(HAVE_SLANG_SLANG_H)
3b411726 29# include <slang/slang.h>
541e6934
KZ
30#endif
31
48d7b13a 32#ifdef HAVE_SLCURSES_H
3b411726 33# include <slcurses.h>
48d7b13a 34#elif defined(HAVE_SLANG_SLCURSES_H)
3b411726 35# include <slang/slcurses.h>
30c97bb8 36#elif defined(HAVE_NCURSESW_NCURSES_H) && defined(HAVE_WIDECHAR)
3b411726 37# include <ncursesw/ncurses.h>
48d7b13a 38#elif defined(HAVE_NCURSES_H)
3b411726 39# include <ncurses.h>
48d7b13a 40#elif defined(HAVE_NCURSES_NCURSES_H)
3b411726 41# include <ncurses/ncurses.h>
2b6fc908 42#endif
541e6934 43
5f94ca33 44#ifdef HAVE_WIDECHAR
3b411726 45# include <wctype.h>
f1512be8 46# include <wchar.h>
5f94ca33
KZ
47#endif
48
8c3a5a44 49#include "c.h"
b2d28533 50#include "closestream.h"
7eda085c 51#include "nls.h"
8abcf290 52#include "strutils.h"
8c3a5a44 53#include "xalloc.h"
5f94ca33 54#include "mbsalign.h"
04915c3f 55#include "colors.h"
57c83d63 56#include "debug.h"
8c3a5a44 57
a4f0a42d
KZ
58#ifdef __GNU__
59# define DEFAULT_DEVICE "/dev/hd0"
60# define ALTERNATE_DEVICE "/dev/sd0"
61#elif defined(__FreeBSD__)
62# define DEFAULT_DEVICE "/dev/ad0"
63# define ALTERNATE_DEVICE "/dev/da0"
64#else
65# define DEFAULT_DEVICE "/dev/sda"
66# define ALTERNATE_DEVICE "/dev/hda"
67#endif
68
c743bf74 69#define ARROW_CURSOR_STRING ">> "
8c3a5a44
KZ
70#define ARROW_CURSOR_DUMMY " "
71#define ARROW_CURSOR_WIDTH (sizeof(ARROW_CURSOR_STRING) - 1)
72
5c04b408
KZ
73/* vertical menu */
74#define MENU_V_SPADDING 1 /* space around menu item string */
75
76/* horizontal menu */
77#define MENU_H_SPADDING 0 /* space around menu item string */
78#define MENU_H_BETWEEN 2 /* space between menu items */
79#define MENU_H_PRESTR "["
80#define MENU_H_POSTSTR "]"
81
82#define MENU_TITLE_PADDING 3
83
84#define MENU_H_PRESTR_SZ (sizeof(MENU_H_PRESTR) - 1)
85#define MENU_H_POSTSTR_SZ (sizeof(MENU_H_POSTSTR) - 1)
86
8c3a5a44 87#define TABLE_START_LINE 4
eef63970
KZ
88#define MENU_START_LINE (ui_lines - 5)
89#define INFO_LINE (ui_lines - 2)
702d0e1c 90#define WARN_LINE INFO_LINE
eef63970 91#define HINT_LINE (ui_lines - 1)
b1f58330
KZ
92
93#define CFDISK_ERR_ESC 5000
94
95#ifndef KEY_ESC
96# define KEY_ESC '\033'
97#endif
98#ifndef KEY_DELETE
99# define KEY_DELETE '\177'
100#endif
1af8003b
KZ
101
102/* colors */
103enum {
104 CFDISK_CL_NONE = 0,
45333e9d
KZ
105 CFDISK_CL_WARNING,
106 CFDISK_CL_FREESPACE,
1af8003b
KZ
107};
108static const int color_pairs[][2] = {
109 /* color foreground, background */
45333e9d
KZ
110 [CFDISK_CL_WARNING] = { COLOR_RED, -1 },
111 [CFDISK_CL_FREESPACE] = { COLOR_GREEN, -1 }
1af8003b 112};
8c3a5a44 113
8460875d 114struct cfdisk;
8460875d 115
3b411726
KZ
116static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx);
117static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf, int key, size_t *idx);
118static struct cfdisk_menu *menu_push(struct cfdisk *cf, struct cfdisk_menuitem *item);
b1f58330 119static struct cfdisk_menu *menu_pop(struct cfdisk *cf);
7556c944 120static void menu_refresh_size(struct cfdisk *cf);
83fa0f80 121
1af8003b 122static int ui_refresh(struct cfdisk *cf);
83fa0f80
KZ
123static void ui_warnx(const char *fmt, ...);
124static void ui_warn(const char *fmt, ...);
125static void ui_info(const char *fmt, ...);
b1f58330 126static void ui_draw_menu(struct cfdisk *cf);
ac27ea5c 127static int ui_menu_move(struct cfdisk *cf, int key);
7556c944 128static void ui_menu_resize(struct cfdisk *cf);
ac27ea5c 129
b1f58330 130static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
05af8bd4 131 uintmax_t low, uintmax_t up, int *expsize);
8460875d 132
83fa0f80 133static int ui_enabled;
7556c944 134static int ui_resize;
8c3a5a44 135
eef63970
KZ
136/* ncurses LINES and COLS may be actual variables or *macros*, but we need
137 * something portable and writable */
138size_t ui_lines;
139size_t ui_cols;
140
88942179 141/* menu item */
3b411726 142struct cfdisk_menuitem {
8c3a5a44
KZ
143 int key; /* keyboard shortcut */
144 const char *name; /* item name */
88942179 145 const char *desc; /* item description (hint) */
6fed9601 146 void *userdata;
749af4b6
KZ
147};
148
88942179 149/* menu */
8c3a5a44 150struct cfdisk_menu {
88942179
KZ
151 char *title; /* optional menu title */
152 struct cfdisk_menuitem *items; /* array with menu items */
153 char *ignore;/* string with keys to ignore */
154 size_t width; /* maximal width of the menu item */
155 size_t nitems; /* number of the active menu items */
156 size_t page_sz;/* when menu longer than screen */
157 size_t idx; /* the current menu item */
8c3a5a44 158 struct cfdisk_menu *prev;
88942179
KZ
159
160 /* @ignore keys generator */
63128bbf 161 int (*ignore_cb) (struct cfdisk *, char *, size_t);
8a726114 162
88942179 163 unsigned int vertical : 1; /* enable vertical mode */
749af4b6
KZ
164};
165
88942179 166/* main menu */
3b411726 167static struct cfdisk_menuitem main_menuitems[] = {
8c3a5a44
KZ
168 { 'b', N_("Bootable"), N_("Toggle bootable flag of the current partition") },
169 { 'd', N_("Delete"), N_("Delete the current partition") },
8c3a5a44 170 { 'n', N_("New"), N_("Create new partition from free space") },
8c3a5a44
KZ
171 { 'q', N_("Quit"), N_("Quit program without writing partition table") },
172 { 't', N_("Type"), N_("Change the partition type") },
314a0dd8 173 { 'h', N_("Help"), N_("Print help screen") },
d5314bf5 174 { 's', N_("Sort"), N_("Fix partitions order") },
8c3a5a44 175 { 'W', N_("Write"), N_("Write partition table to disk (this might destroy data)") },
a89eafed 176 { 'u', N_("Dump"), N_("Dump partition table to sfdisk compatible script file") },
8c3a5a44
KZ
177 { 0, NULL, NULL }
178};
6dbe3af9 179
88942179 180/* top level control struct */
8c3a5a44
KZ
181struct cfdisk {
182 struct fdisk_context *cxt; /* libfdisk context */
183 struct fdisk_table *table; /* partition table */
a6f69126 184 struct cfdisk_menu *menu; /* the current menu */
7eda085c 185
bd85d11f
KZ
186 int *fields; /* output columns IDs */
187 size_t nfields; /* number of columns IDs */
7eda085c 188
8c3a5a44
KZ
189 char *linesbuf; /* table as string */
190 size_t linesbufsz; /* size of the tb_buf */
7eda085c 191
8c3a5a44
KZ
192 char **lines; /* array with lines */
193 size_t nlines; /* number of lines */
a6f69126
KZ
194 size_t lines_idx; /* current line <0..N>, exclude header */
195 size_t page_sz;
d5314bf5 196
bc787727
KZ
197 unsigned int nwrites; /* fdisk_write_disklabel() counter */
198
d121efdb
KZ
199 unsigned int wrong_order :1, /* PT not in right order */
200 zero_start :1; /* ignore existing partition table */
8c3a5a44 201};
5c36a0eb 202
14620822
KZ
203
204/*
205 * let's use include/debug.h stuff for cfdisk too
206 */
207UL_DEBUG_DEFINE_MASK(cfdisk);
819d9a29 208UL_DEBUG_DEFINE_MASKNAMES(cfdisk) = UL_DEBUG_EMPTY_MASKNAMES;
14620822
KZ
209
210#define CFDISK_DEBUG_INIT (1 << 1)
211#define CFDISK_DEBUG_UI (1 << 2)
212#define CFDISK_DEBUG_MENU (1 << 3)
213#define CFDISK_DEBUG_MISC (1 << 4)
214#define CFDISK_DEBUG_TABLE (1 << 5)
215#define CFDISK_DEBUG_ALL 0xFFFF
216
14620822
KZ
217#define DBG(m, x) __UL_DBG(cfdisk, CFDISK_DEBUG_, m, x)
218
219static void cfdisk_init_debug(void)
220{
221 __UL_INIT_DEBUG(cfdisk, CFDISK_DEBUG_, 0, CFDISK_DEBUG);
222}
223
bd85d11f 224/* Initialize output columns -- we follow libfdisk fields (usually specific
88942179
KZ
225 * to the label type.
226 */
8c3a5a44
KZ
227static int cols_init(struct cfdisk *cf)
228{
229 assert(cf);
5c36a0eb 230
bd85d11f
KZ
231 free(cf->fields);
232 cf->fields = NULL;
233 cf->nfields = 0;
5c36a0eb 234
11ee1177 235 return fdisk_label_get_fields_ids(NULL, cf->cxt, &cf->fields, &cf->nfields);
5c36a0eb
KZ
236}
237
7556c944
KZ
238static void resize(void)
239{
240 struct winsize ws;
241
242 if (ioctl(fileno(stdout), TIOCGWINSZ, &ws) != -1
243 && ws.ws_row && ws.ws_col) {
eef63970
KZ
244 ui_lines = ws.ws_row;
245 ui_cols = ws.ws_col;
7556c944
KZ
246#if HAVE_RESIZETERM
247 resizeterm(ws.ws_row, ws.ws_col);
248#endif
249 clearok(stdscr, TRUE);
250 }
251 touchwin(stdscr);
252
eef63970
KZ
253 DBG(UI, ul_debug("ui: resize refresh ui_cols=%zu, ui_lines=%zu",
254 ui_cols, ui_lines));
7556c944
KZ
255 ui_resize = 0;
256}
257
c743bf74
KZ
258/* Reads partition in tree-like order from scols
259 */
260static int partition_from_scols(struct fdisk_table *tb,
261 struct libscols_line *ln)
262{
263 struct fdisk_partition *pa = scols_line_get_userdata(ln);
264
265 fdisk_table_add_partition(tb, pa);
266 fdisk_unref_partition(pa);
267
268 if (scols_line_has_children(ln)) {
269 struct libscols_line *chln;
270 struct libscols_iter *itr = scols_new_iter(SCOLS_ITER_FORWARD);
271
272 if (!itr)
273 return -EINVAL;
274 while (scols_line_next_child(ln, itr, &chln) == 0)
275 partition_from_scols(tb, chln);
a9408274 276 scols_free_iter(itr);
c743bf74
KZ
277 }
278 return 0;
279}
280
8c3a5a44
KZ
281static char *table_to_string(struct cfdisk *cf, struct fdisk_table *tb)
282{
8903f7df 283 struct fdisk_partition *pa;
8c3a5a44
KZ
284 struct fdisk_label *lb;
285 struct fdisk_iter *itr = NULL;
baa3b270
OO
286 struct libscols_table *table = NULL;
287 struct libscols_iter *s_itr = NULL;
8c3a5a44 288 char *res = NULL;
8903f7df 289 size_t i;
45333e9d 290 int tree = 0;
baa3b270 291 struct libscols_line *ln, *ln_cont = NULL;
8c3a5a44 292
14620822 293 DBG(TABLE, ul_debug("convert to string"));
8c3a5a44
KZ
294
295 assert(cf);
296 assert(cf->cxt);
bd85d11f 297 assert(cf->fields);
8c3a5a44
KZ
298 assert(tb);
299
6a632136 300 lb = fdisk_get_label(cf->cxt, NULL);
8c3a5a44
KZ
301 assert(lb);
302
8c3a5a44
KZ
303 itr = fdisk_new_iter(FDISK_ITER_FORWARD);
304 if (!itr)
305 goto done;
5c36a0eb 306
45333e9d
KZ
307 /* get container (e.g. extended partition) */
308 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
309 if (fdisk_partition_is_nested(pa)) {
14620822 310 DBG(TABLE, ul_debug("nested detected, using tree"));
baa3b270 311 tree = SCOLS_FL_TREE;
45333e9d
KZ
312 break;
313 }
314 }
45333e9d 315
0925a9dd 316 table = scols_new_table();
baa3b270 317 if (!table)
45333e9d 318 goto done;
0925a9dd 319 scols_table_enable_maxout(table, 1);
45333e9d 320
8c3a5a44 321 /* headers */
bd85d11f
KZ
322 for (i = 0; i < cf->nfields; i++) {
323 int fl = 0;
324 const struct fdisk_field *field =
325 fdisk_label_get_field(lb, cf->fields[i]);
326 if (!field)
327 continue;
328
329 if (fdisk_field_is_number(field))
330 fl |= SCOLS_FL_RIGHT;
331 if (fdisk_field_get_id(field) == FDISK_FIELD_TYPE)
332 fl |= SCOLS_FL_TRUNC;
333 if (tree && fdisk_field_get_id(field) == FDISK_FIELD_DEVICE)
334 fl |= SCOLS_FL_TREE;
335
336 if (!scols_table_new_column(table,
b6e3c614 337 _(fdisk_field_get_name(field)),
bd85d11f
KZ
338 fdisk_field_get_width(field), fl))
339 goto done;
8c3a5a44
KZ
340 }
341
342 /* data */
8903f7df
KZ
343 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
344
8c3a5a44 345 while (fdisk_table_next_partition(tb, itr, &pa) == 0) {
baa3b270 346 struct libscols_line *parent = fdisk_partition_is_nested(pa) ? ln_cont : NULL;
8903f7df 347
baa3b270 348 ln = scols_table_new_line(table, parent);
8c3a5a44
KZ
349 if (!ln)
350 goto done;
bd85d11f 351 for (i = 0; i < cf->nfields; i++) {
8c3a5a44 352 char *cdata = NULL;
bd85d11f 353
d44115f3 354 if (fdisk_partition_to_string(pa, cf->cxt,
bd85d11f 355 cf->fields[i], &cdata))
8c3a5a44 356 continue;
c743bf74 357 scols_line_refer_data(ln, i, cdata);
8c3a5a44 358 }
8903f7df 359 if (tree && fdisk_partition_is_container(pa))
45333e9d 360 ln_cont = ln;
d051ea93 361
baa3b270 362 scols_line_set_userdata(ln, (void *) pa);
d051ea93 363 fdisk_ref_partition(pa);
8c3a5a44 364 }
6dbe3af9 365
baa3b270 366 if (scols_table_is_empty(table))
d051ea93
KZ
367 goto done;
368
baa3b270
OO
369 scols_table_reduce_termwidth(table, ARROW_CURSOR_WIDTH);
370 scols_print_table_to_string(table, &res);
d051ea93 371
baa3b270 372 /* scols_* code might reorder lines, let's reorder @tb according to the
d051ea93
KZ
373 * final output (it's no problem because partitions are addressed by
374 * parno stored within struct fdisk_partition) */
375
376 /* remove all */
377 fdisk_reset_iter(itr, FDISK_ITER_FORWARD);
378 while (fdisk_table_next_partition(tb, itr, &pa) == 0)
379 fdisk_table_remove_partition(tb, pa);
380
baa3b270
OO
381 s_itr = scols_new_iter(SCOLS_ITER_FORWARD);
382 if (!s_itr)
383 goto done;
384
c743bf74 385 /* add all in the right order (don't forget the output is tree) */
baa3b270 386 while (scols_table_next_line(table, s_itr, &ln) == 0) {
c743bf74
KZ
387 if (scols_line_get_parent(ln))
388 continue;
389 if (partition_from_scols(tb, ln))
390 break;
8c3a5a44
KZ
391 }
392done:
baa3b270
OO
393 scols_unref_table(table);
394 scols_free_iter(s_itr);
8c3a5a44 395 fdisk_free_iter(itr);
6dbe3af9 396
8c3a5a44 397 return res;
2b6fc908
KZ
398}
399
88942179
KZ
400/*
401 * Read data about partitions from libfdisk and prepare output lines.
402 */
8460875d 403static int lines_refresh(struct cfdisk *cf)
8c3a5a44
KZ
404{
405 int rc;
406 char *p;
407 size_t i;
7eda085c 408
8c3a5a44 409 assert(cf);
2b6fc908 410
14620822 411 DBG(TABLE, ul_debug("refreshing buffer"));
c64061c9 412
8c3a5a44
KZ
413 free(cf->linesbuf);
414 free(cf->lines);
415 cf->linesbuf = NULL;
416 cf->linesbufsz = 0;
417 cf->lines = NULL;
418 cf->nlines = 0;
6dbe3af9 419
8c3a5a44 420 fdisk_unref_table(cf->table);
1af8003b 421 cf->table = NULL;
6dbe3af9 422
2cec7949
KZ
423 /* read partitions and free spaces into cf->table */
424 rc = fdisk_get_partitions(cf->cxt, &cf->table);
425 if (!rc)
426 rc = fdisk_get_freespaces(cf->cxt, &cf->table);
8c3a5a44
KZ
427 if (rc)
428 return rc;
0d8589c5 429
8c3a5a44
KZ
430 cf->linesbuf = table_to_string(cf, cf->table);
431 if (!cf->linesbuf)
432 return -ENOMEM;
6dbe3af9 433
8c3a5a44
KZ
434 cf->linesbufsz = strlen(cf->linesbuf);
435 cf->nlines = fdisk_table_get_nents(cf->table) + 1; /* 1 for header line */
a6f69126 436 cf->page_sz = 0;
d5314bf5 437 cf->wrong_order = fdisk_table_wrong_order(cf->table) ? 1 : 0;
a6f69126
KZ
438
439 if (MENU_START_LINE - TABLE_START_LINE < cf->nlines)
440 cf->page_sz = MENU_START_LINE - TABLE_START_LINE - 1;
6dbe3af9 441
6d6d9c1a 442 cf->lines = xcalloc(cf->nlines, sizeof(char *));
6dbe3af9 443
8c3a5a44
KZ
444 for (p = cf->linesbuf, i = 0; p && i < cf->nlines; i++) {
445 cf->lines[i] = p;
446 p = strchr(p, '\n');
447 if (p) {
448 *p = '\0';
449 p++;
450 }
451 }
6dbe3af9 452
8c3a5a44 453 return 0;
6dbe3af9
KZ
454}
455
00b4f26a
KZ
456static struct fdisk_partition *get_current_partition(struct cfdisk *cf)
457{
458 assert(cf);
459 assert(cf->table);
460
461 return fdisk_table_get_partition(cf->table, cf->lines_idx);
462}
463
45333e9d
KZ
464static int is_freespace(struct cfdisk *cf, size_t i)
465{
466 struct fdisk_partition *pa;
467
468 assert(cf);
469 assert(cf->table);
470
471 pa = fdisk_table_get_partition(cf->table, i);
472 return fdisk_partition_is_freespace(pa);
473}
474
b1f58330
KZ
475/* converts libfdisk FDISK_ASKTYPE_MENU to cfdisk menu and returns user's
476 * responseback to libfdisk
477 */
478static int ask_menu(struct fdisk_ask *ask, struct cfdisk *cf)
479{
3b411726 480 struct cfdisk_menuitem *d, *cm;
b1f58330
KZ
481 int key;
482 size_t i = 0, nitems;
483 const char *name, *desc;
484
485 assert(ask);
486 assert(cf);
487
488 /* create cfdisk menu according to libfdisk ask-menu, note that the
489 * last cm[] item has to be empty -- so nitems + 1 */
490 nitems = fdisk_ask_menu_get_nitems(ask);
3b411726 491 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
b1f58330
KZ
492
493 for (i = 0; i < nitems; i++) {
494 if (fdisk_ask_menu_get_item(ask, i, &key, &name, &desc))
495 break;
496 cm[i].key = key;
497 cm[i].desc = desc;
498 cm[i].name = name;
499 }
500
501 /* make the new menu active */
63128bbf 502 menu_push(cf, cm);
b1f58330
KZ
503 ui_draw_menu(cf);
504 refresh();
505
506 /* wait for keys */
507 do {
7ee26cbf 508 key = getch();
ac27ea5c 509
7556c944
KZ
510 if (ui_resize)
511 ui_menu_resize(cf);
ac27ea5c
KZ
512 if (ui_menu_move(cf, key) == 0)
513 continue;
514
515 switch (key) {
b1f58330
KZ
516 case KEY_ENTER:
517 case '\n':
518 case '\r':
6fed9601 519 d = menu_get_menuitem(cf, cf->menu->idx);
b1f58330
KZ
520 if (d)
521 fdisk_ask_menu_set_result(ask, d->key);
522 menu_pop(cf);
523 free(cm);
524 return 0;
525 }
526 } while (1);
527
528 menu_pop(cf);
529 free(cm);
530 return -1;
531}
532
88942179
KZ
533/* libfdisk callback
534 */
8c3a5a44
KZ
535static int ask_callback(struct fdisk_context *cxt, struct fdisk_ask *ask,
536 void *data __attribute__((__unused__)))
537{
538 int rc = 0;
6dbe3af9 539
8c3a5a44
KZ
540 assert(cxt);
541 assert(ask);
6dbe3af9 542
8c3a5a44
KZ
543 switch(fdisk_ask_get_type(ask)) {
544 case FDISK_ASKTYPE_INFO:
83fa0f80 545 ui_info(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
546 break;
547 case FDISK_ASKTYPE_WARNX:
83fa0f80 548 ui_warnx(fdisk_ask_print_get_mesg(ask));
8c3a5a44
KZ
549 break;
550 case FDISK_ASKTYPE_WARN:
83fa0f80 551 ui_warn(fdisk_ask_print_get_mesg(ask));
8c3a5a44 552 break;
b1f58330
KZ
553 case FDISK_ASKTYPE_MENU:
554 ask_menu(ask, (struct cfdisk *) data);
555 break;
8c3a5a44 556 default:
83fa0f80
KZ
557 ui_warnx(_("internal error: unsupported dialog type %d"),
558 fdisk_ask_get_type(ask));
8c3a5a44
KZ
559 return -EINVAL;
560 }
561 return rc;
fd6b7a7f 562}
6dbe3af9 563
7aa0d529 564static int ui_end(void)
8c3a5a44 565{
7aa0d529 566 if (!ui_enabled)
8c3a5a44
KZ
567 return -EINVAL;
568
48d7b13a 569#if defined(HAVE_SLCURSES_H) || defined(HAVE_SLANG_SLCURSES_H)
eef63970 570 SLsmg_gotorc(ui_lines - 1, 0);
8c3a5a44 571 SLsmg_refresh();
2b6fc908 572#else
eef63970 573 mvcur(0, ui_cols - 1, ui_lines-1, 0);
2b6fc908 574#endif
cd8414f7 575 curs_set(1);
8c3a5a44
KZ
576 nl();
577 endwin();
578 printf("\n");
7aa0d529 579 ui_enabled = 0;
8c3a5a44 580 return 0;
6dbe3af9
KZ
581}
582
eef63970 583static void ui_vprint_center(size_t line, int attrs, const char *fmt, va_list ap)
f609e92e 584{
8c3a5a44 585 size_t width;
8c3a5a44 586 char *buf = NULL;
f609e92e 587
8c3a5a44
KZ
588 move(line, 0);
589 clrtoeol();
f609e92e 590
8c3a5a44 591 xvasprintf(&buf, fmt, ap);
fd6b7a7f 592
b1f58330 593 width = mbs_safe_width(buf);
eef63970
KZ
594 if (width > (size_t) ui_cols) {
595 char *p = strrchr(buf + ui_cols, ' ');
91ba41ca 596 if (!p)
eef63970 597 p = buf + ui_cols;
91ba41ca 598 *p = '\0';
eef63970 599 if (line + 1 >= ui_lines)
91ba41ca
KZ
600 line--;
601 attron(attrs);
602 mvaddstr(line, 0, buf);
603 mvaddstr(line + 1, 0, p+1);
604 attroff(attrs);
605 } else {
606 attron(attrs);
eef63970 607 mvaddstr(line, (ui_cols - width) / 2, buf);
91ba41ca
KZ
608 attroff(attrs);
609 }
8c3a5a44 610 free(buf);
6dbe3af9
KZ
611}
612
eef63970 613static void ui_center(size_t line, const char *fmt, ...)
1af8003b
KZ
614{
615 va_list ap;
616 va_start(ap, fmt);
617 ui_vprint_center(line, 0, fmt, ap);
618 va_end(ap);
619}
620
83fa0f80
KZ
621static void ui_warnx(const char *fmt, ...)
622{
623 va_list ap;
624 va_start(ap, fmt);
625 if (ui_enabled)
702d0e1c 626 ui_vprint_center(WARN_LINE,
f1512be8
KZ
627 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
628 fmt, ap);
8fc37981 629 else {
83fa0f80 630 vfprintf(stderr, fmt, ap);
8fc37981
KZ
631 fputc('\n', stderr);
632 }
83fa0f80
KZ
633 va_end(ap);
634}
635
636static void ui_warn(const char *fmt, ...)
1af8003b 637{
83fa0f80 638 char *fmt_m;
1af8003b 639 va_list ap;
83fa0f80
KZ
640
641 xasprintf(&fmt_m, "%s: %m", fmt);
642
1af8003b 643 va_start(ap, fmt);
83fa0f80 644 if (ui_enabled)
702d0e1c 645 ui_vprint_center(WARN_LINE,
f1512be8
KZ
646 colors_wanted() ? COLOR_PAIR(CFDISK_CL_WARNING) : 0,
647 fmt_m, ap);
8fc37981 648 else {
83fa0f80 649 vfprintf(stderr, fmt_m, ap);
8fc37981
KZ
650 fputc('\n', stderr);
651 }
1af8003b 652 va_end(ap);
83fa0f80 653 free(fmt_m);
1af8003b
KZ
654}
655
7aa0d529
KZ
656static int __attribute__((__noreturn__)) ui_errx(int rc, const char *fmt, ...)
657 {
658 va_list ap;
659 ui_end();
660
661 va_start(ap, fmt);
662 fprintf(stderr, "%s: ", program_invocation_short_name);
663 vfprintf(stderr, fmt, ap);
8fc37981 664 fputc('\n', stderr);
7aa0d529
KZ
665 va_end(ap);
666
667 exit(rc);
668}
669
1af8003b
KZ
670static void ui_info(const char *fmt, ...)
671{
672 va_list ap;
673 va_start(ap, fmt);
83fa0f80
KZ
674 if (ui_enabled)
675 ui_vprint_center(INFO_LINE, A_BOLD, fmt, ap);
8fc37981 676 else {
83fa0f80 677 vfprintf(stdout, fmt, ap);
8fc37981
KZ
678 fputc('\n', stdout);
679 }
1af8003b
KZ
680 va_end(ap);
681}
682
683static void ui_clean_info(void)
684{
685 move(INFO_LINE, 0);
686 clrtoeol();
687}
6dbe3af9 688
b1f58330
KZ
689static void ui_hint(const char *fmt, ...)
690{
691 va_list ap;
692 va_start(ap, fmt);
693 if (ui_enabled)
694 ui_vprint_center(HINT_LINE, A_BOLD, fmt, ap);
8fc37981 695 else {
b1f58330 696 vfprintf(stdout, fmt, ap);
8fc37981
KZ
697 fputc('\n', stdout);
698 }
b1f58330
KZ
699 va_end(ap);
700}
701
702static void ui_clean_hint(void)
703{
704 move(HINT_LINE, 0);
705 clrtoeol();
706}
707
8c3a5a44
KZ
708static void die_on_signal(int dummy __attribute__((__unused__)))
709{
14620822 710 DBG(MISC, ul_debug("die on signal."));
7aa0d529 711 ui_end();
8c3a5a44 712 exit(EXIT_FAILURE);
6dbe3af9
KZ
713}
714
7556c944
KZ
715static void resize_on_signal(int dummy __attribute__((__unused__)))
716{
14620822 717 DBG(MISC, ul_debug("resize on signal."));
7556c944
KZ
718 ui_resize = 1;
719}
720
721static void menu_refresh_size(struct cfdisk *cf)
722{
723 if (cf->menu && cf->menu->nitems)
eef63970 724 cf->menu->page_sz = (cf->menu->nitems / (ui_lines - 4)) ? ui_lines - 4 : 0;
7556c944
KZ
725}
726
8c3a5a44
KZ
727static void menu_update_ignore(struct cfdisk *cf)
728{
00b4f26a
KZ
729 char ignore[128] = { 0 };
730 int i = 0;
8c3a5a44 731 struct cfdisk_menu *m;
3b411726 732 struct cfdisk_menuitem *d, *org;
83fa0f80 733 size_t idx;
6dbe3af9 734
8c3a5a44 735 assert(cf);
63128bbf
KZ
736 assert(cf->menu);
737 assert(cf->menu->ignore_cb);
6dbe3af9 738
8c3a5a44 739 m = cf->menu;
6fed9601 740 org = menu_get_menuitem(cf, m->idx);
83fa0f80 741
14620822 742 DBG(MENU, ul_debug("update menu ignored keys"));
6dbe3af9 743
63128bbf 744 i = m->ignore_cb(cf, ignore, sizeof(ignore));
00b4f26a
KZ
745 ignore[i] = '\0';
746
8c3a5a44 747 /* return if no change */
00b4f26a
KZ
748 if ( (!m->ignore && !*ignore)
749 || (m->ignore && *ignore && strcmp(m->ignore, ignore) == 0)) {
8c3a5a44 750 return;
fd6b7a7f 751 }
6dbe3af9 752
8c3a5a44 753 free(m->ignore);
8460875d 754 m->ignore = xstrdup(ignore);
8c3a5a44 755 m->nitems = 0;
6dbe3af9 756
3b411726 757 for (d = m->items; d->name; d++) {
8c3a5a44 758 if (m->ignore && strchr(m->ignore, d->key))
8460875d
KZ
759 continue;
760 m->nitems++;
8c3a5a44 761 }
83fa0f80
KZ
762
763 /* refresh menu index to be at the same menuitem or go to the first */
764 if (org && menu_get_menuitem_by_key(cf, org->key, &idx))
6fed9601 765 m->idx = idx;
83fa0f80 766 else
6fed9601
KZ
767 m->idx = 0;
768
7556c944 769 menu_refresh_size(cf);
6dbe3af9
KZ
770}
771
b1f58330
KZ
772static struct cfdisk_menu *menu_push(
773 struct cfdisk *cf,
3b411726 774 struct cfdisk_menuitem *items)
8c3a5a44
KZ
775{
776 struct cfdisk_menu *m = xcalloc(1, sizeof(*m));
3b411726 777 struct cfdisk_menuitem *d;
6dbe3af9 778
8c3a5a44 779 assert(cf);
6dbe3af9 780
14620822 781 DBG(MENU, ul_debug("new menu"));
6dbe3af9 782
8c3a5a44 783 m->prev = cf->menu;
3b411726 784 m->items = items;
6dbe3af9 785
3b411726 786 for (d = m->items; d->name; d++) {
8c3a5a44 787 const char *name = _(d->name);
b1f58330 788 size_t len = mbs_safe_width(name);
8c3a5a44
KZ
789 if (len > m->width)
790 m->width = len;
791 m->nitems++;
792 }
6dbe3af9 793
8c3a5a44 794 cf->menu = m;
7556c944 795 menu_refresh_size(cf);
8c3a5a44 796 return m;
6dbe3af9
KZ
797}
798
8c3a5a44 799static struct cfdisk_menu *menu_pop(struct cfdisk *cf)
6dbe3af9 800{
8c3a5a44 801 struct cfdisk_menu *m = NULL;
6dbe3af9 802
8c3a5a44 803 assert(cf);
7eda085c 804
14620822 805 DBG(MENU, ul_debug("pop menu"));
7eda085c 806
8c3a5a44
KZ
807 if (cf->menu) {
808 m = cf->menu->prev;
809 free(cf->menu->ignore);
8a726114 810 free(cf->menu->title);
8c3a5a44 811 free(cf->menu);
c07ebfa1 812 }
8c3a5a44
KZ
813 cf->menu = m;
814 return cf->menu;
6dbe3af9
KZ
815}
816
8a726114
KZ
817static void menu_set_title(struct cfdisk_menu *m, const char *title)
818{
819 char *str = NULL;
820
821 if (title) {
822 size_t len = mbs_safe_width(title);
5c04b408
KZ
823 if (len + MENU_TITLE_PADDING > m->width)
824 m->width = len + MENU_TITLE_PADDING;
8a726114
KZ
825 str = xstrdup(title);
826 }
827 m->title = str;
828}
829
830
b1f58330 831static int ui_init(struct cfdisk *cf __attribute__((__unused__)))
8c3a5a44
KZ
832{
833 struct sigaction sa;
6dbe3af9 834
14620822 835 DBG(UI, ul_debug("init"));
6dbe3af9 836
8c3a5a44
KZ
837 /* setup SIGCHLD handler */
838 sigemptyset(&sa.sa_mask);
839 sa.sa_flags = 0;
840 sa.sa_handler = die_on_signal;
841 sigaction(SIGINT, &sa, NULL);
842 sigaction(SIGTERM, &sa, NULL);
6dbe3af9 843
7556c944
KZ
844 sa.sa_handler = resize_on_signal;
845 sigaction(SIGWINCH, &sa, NULL);
846
83fa0f80 847 ui_enabled = 1;
8c3a5a44 848 initscr();
6dbe3af9 849
f1512be8 850#ifdef HAVE_USE_DEFAULT_COLORS
04915c3f 851 if (colors_wanted() && has_colors()) {
1af8003b
KZ
852 size_t i;
853
854 start_color();
855 use_default_colors();
1af8003b
KZ
856 for (i = 1; i < ARRAY_SIZE(color_pairs); i++) /* yeah, start from 1! */
857 init_pair(i, color_pairs[i][0], color_pairs[i][1]);
858 }
f1512be8 859#else
e66a6627 860 colors_off();
f1512be8 861#endif
1af8003b 862
8c3a5a44
KZ
863 cbreak();
864 noecho();
865 nonl();
866 curs_set(0);
867 keypad(stdscr, TRUE);
6dbe3af9 868
8c3a5a44 869 return 0;
7eda085c
KZ
870}
871
5c04b408
KZ
872/* "[ string ]" */
873#define MENU_H_ITEMWIDTH(m) ( MENU_H_PRESTR_SZ \
874 + MENU_H_SPADDING \
875 + (m)->width \
876 + MENU_H_SPADDING \
877 + MENU_H_POSTSTR_SZ)
878
879#define MENU_V_ITEMWIDTH(m) (MENU_V_SPADDING + (m)->width + MENU_V_SPADDING)
880
881
8c3a5a44
KZ
882static size_t menuitem_get_line(struct cfdisk *cf, size_t idx)
883{
6fed9601
KZ
884 struct cfdisk_menu *m = cf->menu;
885
886 if (m->vertical) {
887 if (!m->page_sz) /* small menu */
eef63970 888 return (ui_lines - (cf->menu->nitems + 1)) / 2 + idx;
6fed9601 889 return (idx % m->page_sz) + 1;
8a726114 890 } else {
5c04b408 891 size_t len = MENU_H_ITEMWIDTH(m) + MENU_H_BETWEEN; /** item width */
eef63970 892 size_t items = ui_cols / len; /* items per line */
8a726114 893
a1da27a8
KZ
894 if (items == 0)
895 return 0;
8a726114
KZ
896 return MENU_START_LINE + ((idx / items));
897 }
7eda085c
KZ
898}
899
8c3a5a44
KZ
900static int menuitem_get_column(struct cfdisk *cf, size_t idx)
901{
8a726114 902 if (cf->menu->vertical) {
5c04b408 903 size_t nc = MENU_V_ITEMWIDTH(cf->menu);
eef63970 904 if ((size_t) ui_cols <= nc)
7aa0d529 905 return 0;
eef63970 906 return (ui_cols - nc) / 2;
8a726114 907 } else {
5c04b408 908 size_t len = MENU_H_ITEMWIDTH(cf->menu) + MENU_H_BETWEEN; /* item width */
eef63970 909 size_t items = ui_cols / len; /* items per line */
8a726114 910 size_t extra = items < cf->menu->nitems ? /* extra space on line */
eef63970
KZ
911 ui_cols % len : /* - multi-line menu */
912 ui_cols - (cf->menu->nitems * len); /* - one line menu */
8a726114 913
a1da27a8
KZ
914 if (items == 0)
915 return 0; /* hmm... no space */
916
5c04b408 917 extra += MENU_H_BETWEEN; /* add padding after last item to extra */
e66ac5d3 918
8a726114
KZ
919 if (idx < items)
920 return (idx * len) + (extra / 2);
921 return ((idx % items) * len) + (extra / 2);
922 }
df1dddf9
KZ
923}
924
6fed9601
KZ
925static int menuitem_on_page(struct cfdisk *cf, size_t idx)
926{
927 struct cfdisk_menu *m = cf->menu;
928
929 if (m->page_sz == 0 ||
930 m->idx / m->page_sz == idx / m->page_sz)
931 return 1;
932 return 0;
933}
934
3b411726 935static struct cfdisk_menuitem *menu_get_menuitem(struct cfdisk *cf, size_t idx)
8c3a5a44 936{
3b411726 937 struct cfdisk_menuitem *d;
8c3a5a44 938 size_t i;
7eda085c 939
3b411726 940 for (i = 0, d = cf->menu->items; d->name; d++) {
8c3a5a44
KZ
941 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
942 continue;
943 if (i++ == idx)
944 return d;
d26aa358 945 }
7eda085c 946
8c3a5a44 947 return NULL;
6dbe3af9
KZ
948}
949
3b411726 950static struct cfdisk_menuitem *menu_get_menuitem_by_key(struct cfdisk *cf,
83fa0f80
KZ
951 int key, size_t *idx)
952{
3b411726 953 struct cfdisk_menuitem *d;
83fa0f80 954
3b411726 955 for (*idx = 0, d = cf->menu->items; d->name; d++) {
83fa0f80
KZ
956 if (cf->menu->ignore && strchr(cf->menu->ignore, d->key))
957 continue;
958 if (key == d->key)
959 return d;
960 (*idx)++;
961 }
962
963 return NULL;
964}
965
8c3a5a44 966static void ui_draw_menuitem(struct cfdisk *cf,
3b411726 967 struct cfdisk_menuitem *d,
8c3a5a44
KZ
968 size_t idx)
969{
5c04b408 970 char buf[80 * MB_CUR_MAX], *ptr = buf;
8c3a5a44 971 const char *name;
5c04b408 972 size_t width;
8a726114 973 int ln, cl, vert = cf->menu->vertical;
8c3a5a44 974
6fed9601
KZ
975 if (!menuitem_on_page(cf, idx))
976 return; /* no visible item */
977 ln = menuitem_get_line(cf, idx);
978 cl = menuitem_get_column(cf, idx);
979
5c04b408
KZ
980 /* string width */
981 if (vert) {
982 width = cf->menu->width + MENU_V_SPADDING;
983 memset(ptr, ' ', MENU_V_SPADDING);
984 ptr += MENU_V_SPADDING;
985 } else
986 width = MENU_H_SPADDING + cf->menu->width + MENU_H_SPADDING;
987
8c3a5a44 988 name = _(d->name);
5c04b408 989 mbsalign(name, ptr, sizeof(buf), &width,
8a726114
KZ
990 vert ? MBS_ALIGN_LEFT : MBS_ALIGN_CENTER,
991 0);
8c3a5a44 992
14620822 993 DBG(MENU, ul_debug("menuitem: cl=%d, ln=%d, item='%s'",
8c3a5a44
KZ
994 cl, ln, buf));
995
8a726114
KZ
996 if (vert) {
997 mvaddch(ln, cl - 1, ACS_VLINE);
5c04b408 998 mvaddch(ln, cl + MENU_V_ITEMWIDTH(cf->menu), ACS_VLINE);
8a726114
KZ
999 }
1000
5c04b408 1001 if (cf->menu->idx == idx)
8c3a5a44 1002 standout();
5c04b408
KZ
1003
1004 if (vert)
1005 mvprintw(ln, cl, "%s", buf);
1006 else
1007 mvprintw(ln, cl, "%s%s%s", MENU_H_PRESTR, buf, MENU_H_POSTSTR);
1008
1009 if (cf->menu->idx == idx) {
8c3a5a44
KZ
1010 standend();
1011 if (d->desc)
b6e3c614 1012 ui_hint(_(d->desc));
5c04b408 1013 }
6dbe3af9
KZ
1014}
1015
7556c944
KZ
1016static void ui_clean_menu(struct cfdisk *cf)
1017{
1018 size_t i;
702d0e1c 1019 size_t lastline;
7556c944
KZ
1020 struct cfdisk_menu *m = cf->menu;
1021 size_t ln = menuitem_get_line(cf, 0);
1022
1023 if (m->vertical)
702d0e1c 1024 lastline = ln + (m->page_sz ? m->page_sz : m->nitems);
7556c944 1025 else
702d0e1c 1026 lastline = menuitem_get_line(cf, m->nitems);
7556c944 1027
702d0e1c 1028 for (i = ln; i <= lastline; i++) {
7556c944
KZ
1029 move(i, 0);
1030 clrtoeol();
702d0e1c 1031 DBG(MENU, ul_debug("clean_menu: line %zu", i));
7556c944
KZ
1032 }
1033 if (m->vertical) {
1034 move(ln - 1, 0);
1035 clrtoeol();
1036 }
1037 ui_clean_hint();
1038}
1039
8c3a5a44
KZ
1040static void ui_draw_menu(struct cfdisk *cf)
1041{
3b411726 1042 struct cfdisk_menuitem *d;
8a726114 1043 struct cfdisk_menu *m;
6fed9601
KZ
1044 size_t i = 0;
1045 size_t ln = menuitem_get_line(cf, 0);
1046 size_t nlines;
7eda085c 1047
8c3a5a44
KZ
1048 assert(cf);
1049 assert(cf->menu);
fd6b7a7f 1050
14620822 1051 DBG(MENU, ul_debug("draw start"));
6dbe3af9 1052
7556c944 1053 ui_clean_menu(cf);
8a726114
KZ
1054 m = cf->menu;
1055
6fed9601
KZ
1056 if (m->vertical)
1057 nlines = m->page_sz ? m->page_sz : m->nitems;
1058 else
1059 nlines = menuitem_get_line(cf, m->nitems);
1060
63128bbf
KZ
1061 if (m->ignore_cb)
1062 menu_update_ignore(cf);
00b4f26a 1063 i = 0;
8c3a5a44
KZ
1064 while ((d = menu_get_menuitem(cf, i)))
1065 ui_draw_menuitem(cf, d, i++);
6dbe3af9 1066
8a726114 1067 if (m->vertical) {
8a726114 1068 size_t cl = menuitem_get_column(cf, 0);
6fed9601 1069 size_t curpg = m->page_sz ? m->idx / m->page_sz : 0;
8a726114 1070
6fed9601 1071 /* corners and horizontal lines */
8a726114
KZ
1072 mvaddch(ln - 1, cl - 1, ACS_ULCORNER);
1073 mvaddch(ln + nlines, cl - 1, ACS_LLCORNER);
1074
5c04b408 1075 for (i = 0; i < MENU_V_ITEMWIDTH(m); i++) {
8a726114
KZ
1076 mvaddch(ln - 1, cl + i, ACS_HLINE);
1077 mvaddch(ln + nlines, cl + i, ACS_HLINE);
1078 }
6fed9601 1079
8a726114
KZ
1080 mvaddch(ln - 1, cl + i, ACS_URCORNER);
1081 mvaddch(ln + nlines, cl + i, ACS_LRCORNER);
1082
6fed9601
KZ
1083 /* draw also lines around empty lines on last page */
1084 if (m->page_sz &&
1085 m->nitems / m->page_sz == m->idx / m->page_sz) {
1086 for (i = m->nitems % m->page_sz + 1; i <= m->page_sz; i++) {
1087 mvaddch(i, cl - 1, ACS_VLINE);
5c04b408 1088 mvaddch(i, cl + MENU_V_ITEMWIDTH(m), ACS_VLINE);
6fed9601
KZ
1089 }
1090 }
8a726114
KZ
1091 if (m->title) {
1092 attron(A_BOLD);
1093 mvprintw(ln - 1, cl, " %s ", m->title);
1094 attroff(A_BOLD);
1095 }
6fed9601 1096 if (curpg != 0)
5c04b408 1097 mvaddch(ln - 1, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_UARROW);
6fed9601 1098 if (m->page_sz && curpg < m->nitems / m->page_sz)
5c04b408 1099 mvaddch(ln + nlines, cl + MENU_V_ITEMWIDTH(m) - 2, ACS_DARROW);
8a726114
KZ
1100 }
1101
14620822 1102 DBG(MENU, ul_debug("draw end."));
6dbe3af9
KZ
1103}
1104
8c3a5a44
KZ
1105static void ui_menu_goto(struct cfdisk *cf, int where)
1106{
3b411726 1107 struct cfdisk_menuitem *d;
8c3a5a44
KZ
1108 size_t old;
1109
f019ce1f
KZ
1110 /* stop and begin/end for vertical menus */
1111 if (cf->menu->vertical) {
1112 if (where < 0)
1113 where = 0;
1114 else if (where > (int) cf->menu->nitems - 1)
1115 where = cf->menu->nitems - 1;
1116 } else {
1117 /* continue from begin/end */
1118 if (where < 0)
1119 where = cf->menu->nitems - 1;
1120 else if ((size_t) where > cf->menu->nitems - 1)
1121 where = 0;
1122 }
6fed9601 1123 if ((size_t) where == cf->menu->idx)
8c3a5a44 1124 return;
6dbe3af9 1125
1af8003b
KZ
1126 ui_clean_info();
1127
6fed9601
KZ
1128 old = cf->menu->idx;
1129 cf->menu->idx = where;
1130
1131 if (!menuitem_on_page(cf, old)) {
1132 ui_draw_menu(cf);
1133 return;
1134 }
6dbe3af9 1135
8c3a5a44
KZ
1136 d = menu_get_menuitem(cf, old);
1137 ui_draw_menuitem(cf, d, old);
6dbe3af9 1138
8c3a5a44
KZ
1139 d = menu_get_menuitem(cf, where);
1140 ui_draw_menuitem(cf, d, where);
6dbe3af9
KZ
1141}
1142
ac27ea5c
KZ
1143static int ui_menu_move(struct cfdisk *cf, int key)
1144{
f019ce1f
KZ
1145 struct cfdisk_menu *m;
1146
ac27ea5c
KZ
1147 assert(cf);
1148 assert(cf->menu);
1149
7556c944
KZ
1150 if (key == ERR)
1151 return 0; /* ignore errors */
1152
f019ce1f
KZ
1153 m = cf->menu;
1154
14620822 1155 DBG(MENU, ul_debug("menu move key >%c<.", key));
f019ce1f
KZ
1156
1157 if (m->vertical)
ac27ea5c
KZ
1158 {
1159 switch (key) {
1160 case KEY_DOWN:
1161 case '\016': /* ^N */
1162 case 'j': /* Vi-like alternative */
f019ce1f 1163 ui_menu_goto(cf, m->idx + 1);
ac27ea5c
KZ
1164 return 0;
1165 case KEY_UP:
1166 case '\020': /* ^P */
1167 case 'k': /* Vi-like alternative */
f019ce1f 1168 ui_menu_goto(cf, (int) m->idx - 1);
ac27ea5c 1169 return 0;
f019ce1f
KZ
1170 case KEY_PPAGE:
1171 if (m->page_sz) {
1172 ui_menu_goto(cf, (int) m->idx - m->page_sz);
1173 return 0;
1174 }
d02c2035 1175 /* fallthrough */
ac27ea5c
KZ
1176 case KEY_HOME:
1177 ui_menu_goto(cf, 0);
1178 return 0;
f019ce1f
KZ
1179 case KEY_NPAGE:
1180 if (m->page_sz) {
1181 ui_menu_goto(cf, m->idx + m->page_sz);
1182 return 0;
1183 }
d02c2035 1184 /* fallthrough */
ac27ea5c 1185 case KEY_END:
f019ce1f 1186 ui_menu_goto(cf, m->nitems);
ac27ea5c
KZ
1187 return 0;
1188 }
1189 } else {
1190 switch (key) {
1191 case KEY_RIGHT:
1192 case '\t':
f019ce1f 1193 ui_menu_goto(cf, m->idx + 1);
ac27ea5c
KZ
1194 return 0;
1195 case KEY_LEFT:
1196#ifdef KEY_BTAB
1197 case KEY_BTAB:
1198#endif
f019ce1f 1199 ui_menu_goto(cf, (int) m->idx - 1);
ac27ea5c
KZ
1200 return 0;
1201 }
1202 }
1203
1204 return 1; /* key irrelevant for menu move */
1205}
1206
7556c944
KZ
1207/* but don't call me from ui_run(), this is for pop-up menus only */
1208static void ui_menu_resize(struct cfdisk *cf)
1209{
1210 resize();
1211 ui_clean_menu(cf);
1212 menu_refresh_size(cf);
1213 ui_draw_menu(cf);
1214 refresh();
1215}
1216
a6f69126
KZ
1217static int partition_on_page(struct cfdisk *cf, size_t i)
1218{
1219 if (cf->page_sz == 0 ||
1220 cf->lines_idx / cf->page_sz == i / cf->page_sz)
1221 return 1;
1222 return 0;
1223}
1224
8c3a5a44
KZ
1225static void ui_draw_partition(struct cfdisk *cf, size_t i)
1226{
1227 int ln = TABLE_START_LINE + 1 + i; /* skip table header */
1228 int cl = ARROW_CURSOR_WIDTH; /* we need extra space for cursor */
a6f69126
KZ
1229 int cur = cf->lines_idx == i;
1230 size_t curpg = 0;
1231
1232 if (cf->page_sz) {
1233 if (!partition_on_page(cf, i))
1234 return;
1235 ln = TABLE_START_LINE + (i % cf->page_sz) + 1;
1236 curpg = cf->lines_idx / cf->page_sz;
1237 }
6dbe3af9 1238
14620822
KZ
1239 DBG(UI, ul_debug(
1240 "draw partition %zu [page_sz=%zu, "
a6f69126
KZ
1241 "line=%d, idx=%zu]",
1242 i, cf->page_sz, ln, cf->lines_idx));
6dbe3af9 1243
a6f69126
KZ
1244 if (cur) {
1245 attron(A_REVERSE);
8c3a5a44
KZ
1246 mvaddstr(ln, 0, ARROW_CURSOR_STRING);
1247 mvaddstr(ln, cl, cf->lines[i + 1]);
a6f69126 1248 attroff(A_REVERSE);
6dbe3af9 1249 } else {
45333e9d
KZ
1250 int at = 0;
1251
f1512be8 1252 if (colors_wanted() && is_freespace(cf, i)) {
45333e9d
KZ
1253 attron(COLOR_PAIR(CFDISK_CL_FREESPACE));
1254 at = 1;
1255 }
8c3a5a44
KZ
1256 mvaddstr(ln, 0, ARROW_CURSOR_DUMMY);
1257 mvaddstr(ln, cl, cf->lines[i + 1]);
45333e9d
KZ
1258 if (at)
1259 attroff(COLOR_PAIR(CFDISK_CL_FREESPACE));
6dbe3af9
KZ
1260 }
1261
a6f69126
KZ
1262 if ((size_t) ln == MENU_START_LINE - 1 &&
1263 cf->page_sz && curpg < cf->nlines / cf->page_sz) {
1264 if (cur)
1265 attron(A_REVERSE);
eef63970 1266 mvaddch(ln, ui_cols - 1, ACS_DARROW);
a6f69126
KZ
1267 mvaddch(ln, 0, ACS_DARROW);
1268 if (cur)
1269 attroff(A_REVERSE);
1270 }
6dbe3af9
KZ
1271}
1272
8c3a5a44
KZ
1273static int ui_draw_table(struct cfdisk *cf)
1274{
1275 int cl = ARROW_CURSOR_WIDTH;
1276 size_t i, nparts = fdisk_table_get_nents(cf->table);
a6f69126 1277 size_t curpg = cf->page_sz ? cf->lines_idx / cf->page_sz : 0;
7eda085c 1278
14620822 1279 DBG(UI, ul_debug("draw table"));
6dbe3af9 1280
a6f69126
KZ
1281 for (i = TABLE_START_LINE; i <= TABLE_START_LINE + cf->page_sz; i++) {
1282 move(i, 0);
1283 clrtoeol();
1284 }
6dbe3af9 1285
b2301179
KZ
1286 if ((size_t) cf->lines_idx > nparts - 1)
1287 cf->lines_idx = nparts ? nparts - 1 : 0;
1288
8c3a5a44
KZ
1289 /* print header */
1290 attron(A_BOLD);
1291 mvaddstr(TABLE_START_LINE, cl, cf->lines[0]);
1292 attroff(A_BOLD);
6dbe3af9 1293
8c3a5a44
KZ
1294 /* print partitions */
1295 for (i = 0; i < nparts; i++)
1296 ui_draw_partition(cf, i);
6dbe3af9 1297
a6f69126 1298 if (curpg != 0) {
eef63970 1299 mvaddch(TABLE_START_LINE, ui_cols - 1, ACS_UARROW);
a6f69126
KZ
1300 mvaddch(TABLE_START_LINE, 0, ACS_UARROW);
1301 }
1302 if (cf->page_sz && curpg < cf->nlines / cf->page_sz) {
eef63970 1303 mvaddch(MENU_START_LINE - 1, ui_cols - 1, ACS_DARROW);
a6f69126
KZ
1304 mvaddch(MENU_START_LINE - 1, 0, ACS_DARROW);
1305 }
8c3a5a44 1306 return 0;
6dbe3af9
KZ
1307}
1308
8c3a5a44
KZ
1309static int ui_table_goto(struct cfdisk *cf, int where)
1310{
1311 size_t old;
1312 size_t nparts = fdisk_table_get_nents(cf->table);
6dbe3af9 1313
14620822 1314 DBG(UI, ul_debug("goto table %d", where));
6dbe3af9 1315
8c3a5a44
KZ
1316 if (where < 0)
1317 where = 0;
1318 else if ((size_t) where > nparts - 1)
1319 where = nparts - 1;
6dbe3af9 1320
8c3a5a44
KZ
1321 if ((size_t) where == cf->lines_idx)
1322 return 0;
6dbe3af9 1323
8c3a5a44
KZ
1324 old = cf->lines_idx;
1325 cf->lines_idx = where;
6dbe3af9 1326
a6f69126
KZ
1327 if (!partition_on_page(cf, old) ||!partition_on_page(cf, where))
1328 ui_draw_table(cf);
1329 else {
1330 ui_draw_partition(cf, old); /* cleanup old */
1331 ui_draw_partition(cf, where); /* draw new */
1332 }
1af8003b 1333 ui_clean_info();
8c3a5a44
KZ
1334 ui_draw_menu(cf);
1335 refresh();
1336 return 0;
6dbe3af9
KZ
1337}
1338
8c3a5a44
KZ
1339static int ui_refresh(struct cfdisk *cf)
1340{
57c83d63 1341 struct fdisk_label *lb;
8c3a5a44 1342 char *id = NULL;
57c83d63 1343 uint64_t bytes = fdisk_get_nsectors(cf->cxt) * fdisk_get_sector_size(cf->cxt);
ec8a728a 1344 char *strsz;
8c3a5a44 1345
ec8a728a 1346 erase();
83fa0f80 1347 if (!ui_enabled)
8c3a5a44
KZ
1348 return -EINVAL;
1349
ec8a728a
KZ
1350 strsz = size_to_human_string(SIZE_SUFFIX_SPACE
1351 | SIZE_SUFFIX_3LETTER, bytes);
1352
57c83d63
KZ
1353 lb = fdisk_get_label(cf->cxt, NULL);
1354 assert(lb);
1355
8c3a5a44
KZ
1356 /* header */
1357 attron(A_BOLD);
57c83d63 1358 ui_center(0, _("Disk: %s"), fdisk_get_devname(cf->cxt));
8c3a5a44 1359 attroff(A_BOLD);
1af8003b 1360 ui_center(1, _("Size: %s, %ju bytes, %ju sectors"),
57c83d63 1361 strsz, bytes, (uintmax_t) fdisk_get_nsectors(cf->cxt));
8c3a5a44 1362 if (fdisk_get_disklabel_id(cf->cxt, &id) == 0 && id)
1af8003b 1363 ui_center(2, _("Label: %s, identifier: %s"),
57c83d63 1364 fdisk_label_get_name(lb), id);
7eda085c 1365 else
57c83d63 1366 ui_center(2, _("Label: %s"), fdisk_label_get_name(lb));
8c3a5a44 1367 free(strsz);
6dbe3af9 1368
8c3a5a44
KZ
1369 ui_draw_table(cf);
1370 ui_draw_menu(cf);
6dbe3af9 1371 refresh();
8c3a5a44 1372 return 0;
6dbe3af9
KZ
1373}
1374
b1f58330
KZ
1375static ssize_t ui_get_string(struct cfdisk *cf, const char *prompt,
1376 const char *hint, char *buf, size_t len)
1377{
1378 size_t cells = 0;
1379 ssize_t i = 0, rc = -1;
b1f58330
KZ
1380 int ln = MENU_START_LINE, cl = 1;
1381
1382 assert(cf);
1383 assert(buf);
1384 assert(len);
1385
1386 move(ln, 0);
1387 clrtoeol();
1388
a89eafed
KZ
1389 move(ln + 1, 0);
1390 clrtoeol();
1391
b1f58330 1392 if (prompt) {
f1512be8 1393 mvaddstr(ln, cl, (char *) prompt);
b1f58330
KZ
1394 cl += mbs_safe_width(prompt);
1395 }
1396
1397 /* default value */
1398 if (*buf) {
1399 i = strlen(buf);
1400 cells = mbs_safe_width(buf);
1401 mvaddstr(ln, cl, buf);
1402 }
1403
1404 if (hint)
1405 ui_hint(hint);
1406 else
1407 ui_clean_hint();
1408
1409 move(ln, cl + cells);
1410 curs_set(1);
1411 refresh();
1412
1413 while (1) {
1414#if !defined(HAVE_SLCURSES_H) && !defined(HAVE_SLANG_SLCURSES_H) && \
1415 defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
a08fa9ab 1416 wint_t c;
b1f58330
KZ
1417 if (get_wch(&c) == ERR) {
1418#else
a08fa9ab 1419 int c;
b1f58330
KZ
1420 if ((c = getch()) == ERR) {
1421#endif
7556c944
KZ
1422 if (ui_resize) {
1423 resize();
1424 continue;
1425 }
b1f58330
KZ
1426 if (!isatty(STDIN_FILENO))
1427 exit(2);
1428 else
1429 goto done;
1430 }
1431 if (c == '\r' || c == '\n' || c == KEY_ENTER)
1432 break;
1433
1434 switch (c) {
1435 case KEY_ESC:
1436 rc = -CFDISK_ERR_ESC;
1437 goto done;
1438 case KEY_DELETE:
1439 case '\b':
1440 case KEY_BACKSPACE:
1441 if (i > 0) {
1442 cells--;
1443 i = mbs_truncate(buf, &cells);
1444 if (i < 0)
1445 goto done;
1446 mvaddch(ln, cl + cells, ' ');
1447 move(ln, cl + cells);
1448 } else
1449 beep();
1450 break;
1451 default:
1452#if defined(HAVE_LIBNCURSESW) && defined(HAVE_WIDECHAR)
1453 if (i + 1 < (ssize_t) len && iswprint(c)) {
1454 wchar_t wc = (wchar_t) c;
1455 char s[MB_CUR_MAX + 1];
1456 int sz = wctomb(s, wc);
1457
1458 if (sz > 0 && sz + i < (ssize_t) len) {
1459 s[sz] = '\0';
1460 mvaddnstr(ln, cl + cells, s, sz);
1461 memcpy(buf + i, s, sz);
1462 i += sz;
1463 buf[i] = '\0';
1464 cells += wcwidth(wc);
1465 } else
1466 beep();
1467 }
1468#else
1469 if (i + 1 < (ssize_t) len && isprint(c)) {
1470 mvaddch(ln, cl + cells, c);
f0fb84ab
RM
1471 buf[i++] = c;
1472 buf[i] = '\0';
b1f58330
KZ
1473 cells++;
1474 }
1475#endif
1476 else
1477 beep();
1478 }
1479 refresh();
1480 }
1481
1482 rc = i; /* success */
1483done:
1484 move(ln, 0);
1485 clrtoeol();
1486 curs_set(0);
1487 refresh();
1488
1489 return rc;
1490}
1491
1492/* @res is default value as well as result in bytes */
1493static int ui_get_size(struct cfdisk *cf, const char *prompt, uintmax_t *res,
9b0b9fb1 1494 uintmax_t low, uintmax_t up, int *expsize)
b1f58330
KZ
1495{
1496 char buf[128];
1497 uintmax_t user = 0;
1498 ssize_t rc;
1499 char *dflt = size_to_human_string(0, *res);
1500
14620822 1501 DBG(UI, ul_debug("get_size (default=%ju)", *res));
b1f58330
KZ
1502
1503 ui_clean_info();
1504
1505 do {
91ba41ca 1506 int pwr = 0, insec = 0;
b1f58330
KZ
1507
1508 snprintf(buf, sizeof(buf), "%s", dflt);
1509 rc = ui_get_string(cf, prompt,
a4fbbbd4
BS
1510 _("May be followed by M for MiB, G for GiB, "
1511 "T for TiB, or S for sectors."),
b1f58330
KZ
1512 buf, sizeof(buf));
1513 if (rc == 0) {
1514 ui_warnx(_("Please, specify size."));
1515 continue; /* nothing specified */
1516 } else if (rc == -CFDISK_ERR_ESC)
1517 break; /* cancel dialog */
1518
91ba41ca
KZ
1519 if (strcmp(buf, dflt) == 0)
1520 user = *res, rc = 0; /* no change, use default */
1521 else {
1522 size_t len = strlen(buf);
bfdb3244 1523 if (buf[len - 1] == 'S' || buf[len - 1] == 's') {
91ba41ca
KZ
1524 insec = 1;
1525 buf[len - 1] = '\0';
1526 }
1527 rc = parse_size(buf, &user, &pwr); /* parse */
1528 }
1529
b1f58330 1530 if (rc == 0) {
14620822 1531 DBG(UI, ul_debug("get_size user=%ju, power=%d, sectors=%s",
91ba41ca
KZ
1532 user, pwr, insec ? "yes" : "no"));
1533 if (insec)
57c83d63 1534 user *= fdisk_get_sector_size(cf->cxt);
b1f58330 1535 if (user < low) {
143d9e3e 1536 ui_warnx(_("Minimum size is %ju bytes."), low);
b1f58330
KZ
1537 rc = -ERANGE;
1538 }
1539 if (user > up && pwr && user < up + (1ULL << pwr * 10))
1540 /* ignore when the user specified size overflow
1541 * with in range specified by suffix (e.g. MiB) */
1542 user = up;
1543
1544 if (user > up) {
143d9e3e 1545 ui_warnx(_("Maximum size is %ju bytes."), up);
b1f58330
KZ
1546 rc = -ERANGE;
1547 }
9b0b9fb1
KZ
1548 if (rc == 0 && insec && expsize)
1549 *expsize = 1;
1550
2cec7949
KZ
1551 } else
1552 ui_warnx(_("Failed to parse size."));
b1f58330
KZ
1553 } while (rc != 0);
1554
1555 if (rc == 0)
1556 *res = user;
1557 free(dflt);
1558
14620822 1559 DBG(UI, ul_debug("get_size (result=%ju, rc=%zd)", *res, rc));
b1f58330
KZ
1560 return rc;
1561}
1562
6d6d9c1a
KZ
1563static struct fdisk_parttype *ui_get_parttype(struct cfdisk *cf,
1564 struct fdisk_parttype *cur)
6fed9601 1565{
3b411726 1566 struct cfdisk_menuitem *d, *cm;
6d6d9c1a 1567 size_t i = 0, nitems, idx = 0;
a745611d 1568 struct fdisk_parttype *t = NULL;
5ab37600 1569 struct fdisk_label *lb;
a745611d 1570 int codetypes = 0;
6fed9601 1571
14620822 1572 DBG(UI, ul_debug("asking for parttype."));
6fed9601 1573
5ab37600
KZ
1574 lb = fdisk_get_label(cf->cxt, NULL);
1575
6fed9601
KZ
1576 /* create cfdisk menu according to label types, note that the
1577 * last cm[] item has to be empty -- so nitems + 1 */
a745611d
KZ
1578 nitems = fdisk_label_get_nparttypes(lb);
1579 if (!nitems)
6d6d9c1a 1580 return NULL;
5ab37600 1581
3b411726 1582 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
6fed9601
KZ
1583 if (!cm)
1584 return NULL;
1585
a745611d 1586 codetypes = fdisk_label_has_code_parttypes(lb);
6d6d9c1a 1587
6fed9601 1588 for (i = 0; i < nitems; i++) {
a745611d 1589 const struct fdisk_parttype *x = fdisk_label_get_parttype(lb, i);
6d6d9c1a 1590 char *name;
6fed9601 1591
a745611d
KZ
1592 cm[i].userdata = (void *) x;
1593 if (codetypes)
1594 xasprintf(&name, "%2x %s",
1595 fdisk_parttype_get_code(x),
1596 _(fdisk_parttype_get_name(x)));
6d6d9c1a 1597 else {
a745611d
KZ
1598 name = (char *) _(fdisk_parttype_get_name(x));
1599 cm[i].desc = fdisk_parttype_get_string(x);
6d6d9c1a
KZ
1600 }
1601 cm[i].name = name;
1602 if (x == cur)
1603 idx = i;
6fed9601
KZ
1604 }
1605
1606 /* make the new menu active */
1607 menu_push(cf, cm);
1608 cf->menu->vertical = 1;
6d6d9c1a 1609 cf->menu->idx = idx;
6fed9601
KZ
1610 menu_set_title(cf->menu, _("Select partition type"));
1611 ui_draw_menu(cf);
1612 refresh();
1613
1614 do {
1615 int key = getch();
f019ce1f 1616
7556c944
KZ
1617 if (ui_resize)
1618 ui_menu_resize(cf);
6fed9601
KZ
1619 if (ui_menu_move(cf, key) == 0)
1620 continue;
f019ce1f 1621
6fed9601
KZ
1622 switch (key) {
1623 case KEY_ENTER:
1624 case '\n':
1625 case '\r':
1626 d = menu_get_menuitem(cf, cf->menu->idx);
1627 if (d)
1628 t = (struct fdisk_parttype *) d->userdata;
1629 goto done;
ad5d45c0 1630 case KEY_ESC:
6fed9601
KZ
1631 case 'q':
1632 case 'Q':
1633 goto done;
1634 }
1635 } while (1);
1636
1637done:
1638 menu_pop(cf);
a745611d 1639 if (codetypes) {
6d6d9c1a
KZ
1640 for (i = 0; i < nitems; i++)
1641 free((char *) cm[i].name);
1642 }
6fed9601 1643 free(cm);
57c83d63
KZ
1644 DBG(UI, ul_debug("get parrtype done [type=%s] ", t ?
1645 fdisk_parttype_get_name(t) : NULL));
6fed9601
KZ
1646 return t;
1647}
1648
a89eafed
KZ
1649static int ui_script_read(struct cfdisk *cf)
1650{
1651 struct fdisk_script *sc = NULL;
1652 char buf[PATH_MAX] = { 0 };
1653 int rc;
1654
1655 rc = ui_get_string(cf, _("Enter script file name: "),
1656 _("The script file will be applied to in-memory partition table."),
1657 buf, sizeof(buf));
1658 if (rc <= 0)
1659 return rc;
1660
1661 rc = -1;
1662 errno = 0;
1663 sc = fdisk_new_script_from_file(cf->cxt, buf);
1664 if (!sc && errno)
54fefa07 1665 ui_warn(_("Cannot open %s"), buf);
a89eafed
KZ
1666 else if (!sc)
1667 ui_warnx(_("Failed to parse script file %s"), buf);
1668 else if (fdisk_apply_script(cf->cxt, sc) != 0)
1669 ui_warnx(_("Failed to apply script %s"), buf);
1670 else
1671 rc = 0;
1672
1673 fdisk_unref_script(sc);
1674 return rc;
1675}
1676
1677static int ui_script_write(struct cfdisk *cf)
1678{
1679 struct fdisk_script *sc = NULL;
1680 char buf[PATH_MAX] = { 0 };
1681 FILE *f = NULL;
1682 int rc;
1683
1684 rc = ui_get_string(cf, _("Enter script file name: "),
1685 _("The current in-memory partition table will be dumped to the file."),
1686 buf, sizeof(buf));
1687 if (rc <= 0)
1688 return rc;
1689
1690 rc = 0;
1691 sc = fdisk_new_script(cf->cxt);
1692 if (!sc) {
1693 ui_warn(_("Failed to allocate script handler"));
1694 goto done;
1695 }
1696
1697 rc = fdisk_script_read_context(sc, NULL);
1698 if (rc) {
1699 ui_warnx(_("Failed to read disk layout into script."));
1700 goto done;
1701 }
1702
8c9615a9 1703 DBG(UI, ul_debug("writing dump into: '%s'", buf));
a89eafed
KZ
1704 f = fopen(buf, "w");
1705 if (!f) {
54fefa07 1706 ui_warn(_("Cannot open %s"), buf);
8c9615a9 1707 rc = -errno;
a89eafed
KZ
1708 goto done;
1709 }
1710
1711 rc = fdisk_script_write_file(sc, f);
8c9615a9
KZ
1712 if (!rc)
1713 ui_info(_("Disk layout successfully dumped."));
1714done:
a89eafed
KZ
1715 if (rc)
1716 ui_warn(_("Failed to write script %s"), buf);
a89eafed
KZ
1717 if (f)
1718 fclose(f);
1719 fdisk_unref_script(sc);
1720 return rc;
1721}
1722
7aa0d529
KZ
1723/* prints menu with libfdisk labels and waits for users response */
1724static int ui_create_label(struct cfdisk *cf)
1725{
3b411726 1726 struct cfdisk_menuitem *d, *cm;
7aa0d529
KZ
1727 int rc = 1;
1728 size_t i = 0, nitems;
1729 struct fdisk_label *lb = NULL;
1730
1731 assert(cf);
1732
14620822 1733 DBG(UI, ul_debug("asking for new disklabe."));
7aa0d529
KZ
1734
1735 /* create cfdisk menu according to libfdisk labels, note that the
1736 * last cm[] item has to be empty -- so nitems + 1 */
6a632136 1737 nitems = fdisk_get_nlabels(cf->cxt);
3b411726 1738 cm = xcalloc(nitems + 1, sizeof(struct cfdisk_menuitem));
7aa0d529 1739
6a632136 1740 while (fdisk_next_label(cf->cxt, &lb) == 0) {
57c83d63 1741 if (fdisk_label_is_disabled(lb) ||
8eccde20 1742 fdisk_label_get_type(lb) == FDISK_DISKLABEL_BSD)
dd626abd 1743 continue;
57c83d63 1744 cm[i++].name = fdisk_label_get_name(lb);
7aa0d529
KZ
1745 }
1746
1747 erase();
eef63970 1748 ui_center(ui_lines - 4,
7aa0d529 1749 _("Device does not contain a recognized partition table."));
eef63970 1750 ui_center(ui_lines - 3,
a89eafed 1751 _("Select a type to create a new label or press 'L' to load script file."));
7aa0d529
KZ
1752
1753 /* make the new menu active */
63128bbf 1754 menu_push(cf, cm);
7aa0d529
KZ
1755 cf->menu->vertical = 1;
1756 menu_set_title(cf->menu, _("Select label type"));
1757 ui_draw_menu(cf);
1758 refresh();
1759
1760 do {
1761 int key = getch();
7556c944
KZ
1762
1763 if (ui_resize)
1764 ui_menu_resize(cf);
7aa0d529
KZ
1765 if (ui_menu_move(cf, key) == 0)
1766 continue;
1767 switch (key) {
1768 case KEY_ENTER:
1769 case '\n':
1770 case '\r':
6fed9601 1771 d = menu_get_menuitem(cf, cf->menu->idx);
7aa0d529
KZ
1772 if (d)
1773 rc = fdisk_create_disklabel(cf->cxt, d->name);
1774 goto done;
ad5d45c0 1775 case KEY_ESC:
7aa0d529
KZ
1776 case 'q':
1777 case 'Q':
1778 goto done;
a89eafed
KZ
1779 case 'L':
1780 ui_clean_hint();
1781 ui_clean_info();
1782
1783 rc = ui_script_read(cf);
1784 if (rc == 0)
1785 goto done;
1786 break;
7aa0d529
KZ
1787 }
1788 } while (1);
1789
1790done:
1791 menu_pop(cf);
1792 free(cm);
14620822 1793 DBG(UI, ul_debug("create label done [rc=%d] ", rc));
7aa0d529
KZ
1794 return rc;
1795}
1796
a89eafed 1797
314a0dd8
KZ
1798static int ui_help(void)
1799{
1800 size_t i;
314a0dd8 1801 static const char *help[] = {
e993b9c8
BS
1802 N_("This is cfdisk, a curses-based disk partitioning program."),
1803 N_("It lets you create, delete, and modify partitions on a block device."),
fd4484a7 1804 " ",
314a0dd8
KZ
1805 N_("Command Meaning"),
1806 N_("------- -------"),
1807 N_(" b Toggle bootable flag of the current partition"),
1808 N_(" d Delete the current partition"),
1809 N_(" h Print this screen"),
1810 N_(" n Create new partition from free space"),
1811 N_(" q Quit program without writing partition table"),
33f7c502 1812 N_(" s Fix partitions order (only when in disarray)"),
e993b9c8 1813 N_(" t Change the partition type"),
a89eafed 1814 N_(" u Dump disk layout to sfdisk compatible script file"),
e993b9c8
BS
1815 N_(" W Write partition table to disk (you must enter uppercase W);"),
1816 N_(" since this might destroy data on the disk, you must either"),
1817 N_(" confirm or deny the write by entering 'yes' or 'no'"),
314a0dd8
KZ
1818 N_("Up Arrow Move cursor to the previous partition"),
1819 N_("Down Arrow Move cursor to the next partition"),
1820 N_("Left Arrow Move cursor to the previous menu item"),
1821 N_("Right Arrow Move cursor to the next menu item"),
fd4484a7 1822 " ",
314a0dd8 1823 N_("Note: All of the commands can be entered with either upper or lower"),
e993b9c8 1824 N_("case letters (except for Write)."),
fd4484a7 1825 " ",
314a0dd8
KZ
1826 N_("Use lsblk(8) or partx(8) to see more details about the device.")
1827 };
1828
1829 erase();
314a0dd8
KZ
1830 for (i = 0; i < ARRAY_SIZE(help); i++)
1831 mvaddstr(i, 1, _(help[i]));
1832
1833 ui_info(_("Press a key to continue."));
7556c944 1834
314a0dd8
KZ
1835 getch();
1836 return 0;
1837}
1838
63128bbf
KZ
1839/* TODO: use @sz, now 128bytes */
1840static int main_menu_ignore_keys(struct cfdisk *cf, char *ignore,
1841 size_t sz __attribute__((__unused__)))
1842{
1843 struct fdisk_partition *pa = get_current_partition(cf);
1844 size_t i = 0;
1845
1846 if (!pa)
1847 return 0;
1848 if (fdisk_partition_is_freespace(pa)) {
1849 ignore[i++] = 'd'; /* delete */
1850 ignore[i++] = 't'; /* set type */
1851 ignore[i++] = 'b'; /* set bootable */
1852 } else {
1853 ignore[i++] = 'n';
aa36c2cf
KZ
1854 if (!fdisk_is_label(cf->cxt, DOS) &&
1855 !fdisk_is_label(cf->cxt, SGI))
63128bbf
KZ
1856 ignore[i++] = 'b';
1857 }
e146ae4e 1858
d5314bf5
KZ
1859 if (!cf->wrong_order)
1860 ignore[i++] = 's';
6a632136 1861 if (fdisk_is_readonly(cf->cxt))
e146ae4e 1862 ignore[i++] = 'W';
63128bbf
KZ
1863 return i;
1864}
1865
1866
ed8a13e6
KZ
1867/* returns: error: < 0, success: 0, quit: 1 */
1868static int main_menu_action(struct cfdisk *cf, int key)
1869{
1870 size_t n;
d5314bf5 1871 int ref = 0, rc, org_order = cf->wrong_order;
ed8a13e6
KZ
1872 const char *info = NULL, *warn = NULL;
1873 struct fdisk_partition *pa;
1874
1875 assert(cf);
1876 assert(cf->cxt);
1877 assert(cf->menu);
1878
1879 if (key == 0) {
3b411726 1880 struct cfdisk_menuitem *d = menu_get_menuitem(cf, cf->menu->idx);
ed8a13e6
KZ
1881 if (!d)
1882 return 0;
1883 key = d->key;
1884
1885 } else if (key != 'w' && key != 'W')
1886 key = tolower(key); /* case insensitive except 'W'rite */
1887
14620822 1888 DBG(MENU, ul_debug("main menu action: key=%c", key));
ed8a13e6
KZ
1889
1890 if (cf->menu->ignore && strchr(cf->menu->ignore, key)) {
14620822 1891 DBG(MENU, ul_debug(" ignore '%c'", key));
ed8a13e6
KZ
1892 return 0;
1893 }
1894
1895 pa = get_current_partition(cf);
ec8a728a
KZ
1896 if (!pa)
1897 return -EINVAL;
ed8a13e6
KZ
1898 n = fdisk_partition_get_partno(pa);
1899
14620822 1900 DBG(MENU, ul_debug("menu action on %p", pa));
7b1ffbc4
KZ
1901 ui_clean_hint();
1902 ui_clean_info();
ed8a13e6
KZ
1903
1904 switch (key) {
1905 case 'b': /* Bootable flag */
1906 {
aa36c2cf
KZ
1907 int fl = fdisk_is_label(cf->cxt, DOS) ? DOS_FLAG_ACTIVE :
1908 fdisk_is_label(cf->cxt, SGI) ? SGI_FLAG_BOOT : 0;
ed8a13e6 1909
a1ef792f 1910 if (fl && fdisk_toggle_partition_flag(cf->cxt, n, fl))
ed8a13e6
KZ
1911 warn = _("Could not toggle the flag.");
1912 else if (fl)
1913 ref = 1;
1914 break;
1915 }
f1512be8 1916#ifdef KEY_DC
f019ce1f 1917 case KEY_DC:
f1512be8 1918#endif
ed8a13e6
KZ
1919 case 'd': /* Delete */
1920 if (fdisk_delete_partition(cf->cxt, n) != 0)
1921 warn = _("Could not delete partition %zu.");
1922 else
1923 info = _("Partition %zu has been deleted.");
1924 ref = 1;
1925 break;
314a0dd8
KZ
1926 case 'h': /* help */
1927 ui_help();
1928 ref = 1;
1929 break;
ed8a13e6
KZ
1930 case 'n': /* New */
1931 {
75090201 1932 uint64_t start, size, dflt_size, secs;
ed8a13e6 1933 struct fdisk_partition *npa; /* the new partition */
9b0b9fb1 1934 int expsize = 0; /* size specified explicitly in sectors */
ed8a13e6 1935
ec8a728a 1936 if (!fdisk_partition_is_freespace(pa) || !fdisk_partition_has_start(pa))
ed8a13e6 1937 return -EINVAL;
75090201 1938
ed8a13e6
KZ
1939 /* free space range */
1940 start = fdisk_partition_get_start(pa);
57c83d63 1941 size = dflt_size = fdisk_partition_get_size(pa) * fdisk_get_sector_size(cf->cxt);
ed8a13e6 1942
9b0b9fb1 1943 if (ui_get_size(cf, _("Partition size: "), &size, 1, size, &expsize)
ed8a13e6
KZ
1944 == -CFDISK_ERR_ESC)
1945 break;
1946
75090201
KZ
1947 secs = size / fdisk_get_sector_size(cf->cxt);
1948 if (size && secs < 1) {
1949 warn = _("Too small partition size specified.");
1950 break;
1951 }
1952
1953 npa = fdisk_new_partition();
1954 if (!npa)
1955 return -ENOMEM;
1956
ed8a13e6
KZ
1957 if (dflt_size == size) /* default is to fillin all free space */
1958 fdisk_partition_end_follow_default(npa, 1);
75090201
KZ
1959 else
1960 fdisk_partition_set_size(npa, secs);
ed8a13e6 1961
9b0b9fb1
KZ
1962 if (expsize)
1963 fdisk_partition_size_explicit(pa, 1);
1964
ed8a13e6 1965 fdisk_partition_set_start(npa, start);
9b0b9fb1 1966 fdisk_partition_partno_follow_default(npa, 1);
ed8a13e6 1967 /* add to disk label -- libfdisk will ask for missing details */
c3bc7483 1968 rc = fdisk_add_partition(cf->cxt, npa, NULL);
ed8a13e6
KZ
1969 fdisk_unref_partition(npa);
1970 if (rc == 0)
1971 ref = 1;
1972 break;
1973 }
1974 case 'q': /* Quit */
1975 return 1;
1976 case 't': /* Type */
6fed9601 1977 {
6fed9601
KZ
1978 struct fdisk_parttype *t;
1979
ec8a728a 1980 if (fdisk_partition_is_freespace(pa))
6fed9601
KZ
1981 return -EINVAL;
1982 t = (struct fdisk_parttype *) fdisk_partition_get_type(pa);
6d6d9c1a 1983 t = ui_get_parttype(cf, t);
6fed9601
KZ
1984 ref = 1;
1985
1986 if (t && fdisk_set_partition_type(cf->cxt, n, t) == 0)
09af3db4 1987 info = _("Changed type of partition %zu.");
6fed9601 1988 else
09af3db4 1989 info = _("The type of partition %zu is unchanged.");
ed8a13e6 1990 break;
6fed9601 1991 }
d5314bf5
KZ
1992 case 's': /* fix order */
1993 if (cf->wrong_order) {
1994 fdisk_reorder_partitions(cf->cxt);
1995 ref = 1;
1996 }
1997 break;
a89eafed 1998 case 'u':
8c9615a9 1999 ui_script_write(cf);
a89eafed 2000 break;
ed8a13e6 2001 case 'W': /* Write */
7b1ffbc4
KZ
2002 {
2003 char buf[64] = { 0 };
e146ae4e 2004
6a632136 2005 if (fdisk_is_readonly(cf->cxt)) {
33f7c502 2006 warn = _("Device open in read-only mode");
e146ae4e
KZ
2007 break;
2008 }
2009
2010 rc = ui_get_string(cf,
7b1ffbc4
KZ
2011 _("Are you sure you want to write the partition "
2012 "table to disk? "),
09af3db4 2013 _("Type \"yes\" or \"no\", or press ESC to leave this dialog."),
7b1ffbc4
KZ
2014 buf, sizeof(buf));
2015
2016 ref = 1;
59af21c3
KZ
2017 if (rc <= 0 || (strcasecmp(buf, "yes") != 0 &&
2018 strcasecmp(buf, _("yes")) != 0)) {
7b1ffbc4
KZ
2019 info = _("Did not write partition table to disk");
2020 break;
2021 }
ed8a13e6
KZ
2022 rc = fdisk_write_disklabel(cf->cxt);
2023 if (rc)
7b1ffbc4
KZ
2024 warn = _("Failed to write disklabel");
2025 else {
2026 fdisk_reread_partition_table(cf->cxt);
2027 info = _("The partition table has been altered.");
2028 }
bc787727 2029 cf->nwrites++;
7b1ffbc4
KZ
2030 break;
2031 }
2032 default:
ed8a13e6
KZ
2033 break;
2034 }
2035
2036 if (ref) {
2037 lines_refresh(cf);
2038 ui_refresh(cf);
a89eafed
KZ
2039 } else
2040 ui_draw_menu(cf);
7b1ffbc4
KZ
2041
2042 ui_clean_hint();
75090201 2043
ed8a13e6 2044 if (warn)
7b1ffbc4 2045 ui_warnx(warn, n + 1);
ed8a13e6 2046 else if (info)
7b1ffbc4 2047 ui_info(info, n + 1);
d5314bf5
KZ
2048 else if (key == 'n' && cf->wrong_order && org_order == 0)
2049 ui_info(_("Note that partition table entries are not in disk order now."));
ed8a13e6
KZ
2050
2051 return 0;
2052}
2053
7556c944
KZ
2054static void ui_resize_refresh(struct cfdisk *cf)
2055{
2056 resize();
2057 menu_refresh_size(cf);
2058 lines_refresh(cf);
2059 ui_refresh(cf);
2060}
2061
8c3a5a44
KZ
2062static int ui_run(struct cfdisk *cf)
2063{
7aa0d529 2064 int rc = 0;
6dbe3af9 2065
eef63970
KZ
2066 ui_lines = LINES;
2067 ui_cols = COLS;
2068 DBG(UI, ul_debug("start cols=%zu, lines=%zu", ui_cols, ui_lines));
6dbe3af9 2069
aa36c2cf 2070 if (!fdisk_has_label(cf->cxt) || cf->zero_start) {
7aa0d529
KZ
2071 rc = ui_create_label(cf);
2072 if (rc < 0)
2073 ui_errx(EXIT_FAILURE,
2074 _("failed to create a new disklabel"));
2075 if (rc)
2076 return rc;
2077 }
2078
2079 cols_init(cf);
2080 rc = lines_refresh(cf);
2081 if (rc)
2082 ui_errx(EXIT_FAILURE, _("failed to read partitions"));
6dbe3af9 2083
3b411726 2084 menu_push(cf, main_menuitems);
63128bbf
KZ
2085 cf->menu->ignore_cb = main_menu_ignore_keys;
2086
8c3a5a44
KZ
2087 rc = ui_refresh(cf);
2088 if (rc)
2089 return rc;
6dbe3af9 2090
6a632136 2091 if (fdisk_is_readonly(cf->cxt))
e146ae4e
KZ
2092 ui_warnx(_("Device open in read-only mode."));
2093
8c3a5a44 2094 do {
7ee26cbf 2095 int key = getch();
6dbe3af9 2096
7ee26cbf 2097 rc = 0;
7556c944
KZ
2098 if (ui_resize)
2099 /* Note that ncurses getch() returns ERR when interrupted
2100 * by signal, but SLang does not interrupt at all. */
2101 ui_resize_refresh(cf);
2102 if (key == ERR)
2103 continue;
ac27ea5c
KZ
2104 if (ui_menu_move(cf, key) == 0)
2105 continue;
2106
14620822 2107 DBG(UI, ul_debug("main action key >%c<.", key));
f019ce1f 2108
8c3a5a44
KZ
2109 switch (key) {
2110 case KEY_DOWN:
2111 case '\016': /* ^N */
2112 case 'j': /* Vi-like alternative */
2113 ui_table_goto(cf, cf->lines_idx + 1);
2114 break;
2115 case KEY_UP:
2116 case '\020': /* ^P */
2117 case 'k': /* Vi-like alternative */
f019ce1f 2118 ui_table_goto(cf, (int) cf->lines_idx - 1);
8c3a5a44 2119 break;
f019ce1f
KZ
2120 case KEY_PPAGE:
2121 if (cf->page_sz) {
2122 ui_table_goto(cf, (int) cf->lines_idx - cf->page_sz);
2123 break;
2124 }
8c3a5a44
KZ
2125 case KEY_HOME:
2126 ui_table_goto(cf, 0);
2127 break;
f019ce1f
KZ
2128 case KEY_NPAGE:
2129 if (cf->page_sz) {
2130 ui_table_goto(cf, cf->lines_idx + cf->page_sz);
2131 break;
2132 }
8c3a5a44 2133 case KEY_END:
f019ce1f 2134 ui_table_goto(cf, (int) cf->nlines - 1);
8c3a5a44 2135 break;
8c3a5a44
KZ
2136 case KEY_ENTER:
2137 case '\n':
2138 case '\r':
ed8a13e6 2139 rc = main_menu_action(cf, 0);
8c3a5a44
KZ
2140 break;
2141 default:
ed8a13e6 2142 rc = main_menu_action(cf, key);
8460875d 2143 if (rc < 0)
8c3a5a44
KZ
2144 beep();
2145 break;
2146 }
8460875d
KZ
2147
2148 if (rc == 1)
2149 break; /* quit */
8c3a5a44 2150 } while (1);
6dbe3af9 2151
8c3a5a44 2152 menu_pop(cf);
6dbe3af9 2153
14620822 2154 DBG(UI, ul_debug("end"));
8c3a5a44
KZ
2155 return 0;
2156}
6dbe3af9 2157
04915c3f
KZ
2158static void __attribute__ ((__noreturn__)) usage(FILE *out)
2159{
2160 fputs(USAGE_HEADER, out);
04915c3f
KZ
2161 fprintf(out,
2162 _(" %1$s [options] <disk>\n"), program_invocation_short_name);
2163
451dbcfa
BS
2164 fputs(USAGE_SEPARATOR, out);
2165 fputs(_("Display or manipulate a disk partition table.\n"), out);
2166
04915c3f
KZ
2167 fputs(USAGE_OPTIONS, out);
2168 fputs(_(" -L --color[=<when>] colorize output (auto, always or never)\n"), out);
d121efdb 2169 fputs(_(" -z --zero start with zeroed partition table\n"), out);
04915c3f
KZ
2170
2171 fputs(USAGE_SEPARATOR, out);
2172 fputs(USAGE_HELP, out);
2173 fputs(USAGE_VERSION, out);
2174
2175 fprintf(out, USAGE_MAN_TAIL("cfdisk(8)"));
2176 exit(out == stderr ? EXIT_FAILURE : EXIT_SUCCESS);
2177}
2178
8c3a5a44
KZ
2179int main(int argc, char *argv[])
2180{
a4f0a42d 2181 const char *diskpath;
ad9fe47e 2182 int rc, c, colormode = UL_COLORMODE_UNDEF;
8c3a5a44
KZ
2183 struct cfdisk _cf = { .lines_idx = 0 },
2184 *cf = &_cf;
6dbe3af9 2185
04915c3f
KZ
2186 static const struct option longopts[] = {
2187 { "color", optional_argument, NULL, 'L' },
2188 { "help", no_argument, NULL, 'h' },
2189 { "version", no_argument, NULL, 'V' },
d121efdb 2190 { "zero", no_argument, NULL, 'z' },
04915c3f
KZ
2191 { NULL, 0, 0, 0 },
2192 };
2193
8c3a5a44
KZ
2194 setlocale(LC_ALL, "");
2195 bindtextdomain(PACKAGE, LOCALEDIR);
2196 textdomain(PACKAGE);
2197 atexit(close_stdout);
6dbe3af9 2198
d121efdb 2199 while((c = getopt_long(argc, argv, "L::hVz", longopts, NULL)) != -1) {
04915c3f
KZ
2200 switch(c) {
2201 case 'h':
2202 usage(stdout);
2203 break;
2204 case 'L':
2205 colormode = UL_COLORMODE_AUTO;
2206 if (optarg)
2207 colormode = colormode_or_err(optarg,
2208 _("unsupported color mode"));
2209 break;
2210 case 'V':
f6277500 2211 printf(UTIL_LINUX_VERSION);
04915c3f 2212 return EXIT_SUCCESS;
d121efdb
KZ
2213 case 'z':
2214 cf->zero_start = 1;
2215 break;
04915c3f
KZ
2216 }
2217 }
2218
210bb492 2219 colors_init(colormode, "cfdisk");
04915c3f 2220
8c3a5a44 2221 fdisk_init_debug(0);
710ed55d 2222 scols_init_debug(0);
14620822 2223 cfdisk_init_debug();
8c3a5a44
KZ
2224 cf->cxt = fdisk_new_context();
2225 if (!cf->cxt)
2226 err(EXIT_FAILURE, _("failed to allocate libfdisk context"));
6dbe3af9 2227
6a632136 2228 fdisk_set_ask(cf->cxt, ask_callback, (void *) cf);
6dbe3af9 2229
04915c3f 2230 if (optind == argc)
a4f0a42d
KZ
2231 diskpath = access(DEFAULT_DEVICE, F_OK) == 0 ?
2232 DEFAULT_DEVICE : ALTERNATE_DEVICE;
2233 else
2234 diskpath = argv[optind];
6dbe3af9 2235
6a632136 2236 rc = fdisk_assign_device(cf->cxt, diskpath, 0);
e146ae4e 2237 if (rc == -EACCES)
6a632136 2238 rc = fdisk_assign_device(cf->cxt, diskpath, 1);
e146ae4e 2239 if (rc != 0)
a4f0a42d
KZ
2240 err(EXIT_FAILURE, _("cannot open %s"),
2241 optind == argc ? DEFAULT_DEVICE : diskpath);
6dbe3af9 2242
8c3a5a44
KZ
2243 /* Don't use err(), warn() from this point */
2244 ui_init(cf);
2245 ui_run(cf);
7aa0d529 2246 ui_end();
6dbe3af9 2247
8c3a5a44
KZ
2248 free(cf->lines);
2249 free(cf->linesbuf);
2250 fdisk_unref_table(cf->table);
ed8a13e6 2251
6a632136 2252 rc = fdisk_deassign_device(cf->cxt, cf->nwrites == 0);
c7119037 2253 fdisk_unref_context(cf->cxt);
14620822 2254 DBG(MISC, ul_debug("bye! [rc=%d]", rc));
ed8a13e6 2255 return rc == 0 ? EXIT_SUCCESS : EXIT_FAILURE;
6dbe3af9 2256}