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