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