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