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