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