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