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