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