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