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