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